diff --git a/.pkgmeta b/.pkgmeta
deleted file mode 100644
index 5dde371..0000000
--- a/.pkgmeta
+++ /dev/null
@@ -1,21 +0,0 @@
-package-as: Omen
-
-externals:
- Libs/LibStub: svn://svn.wowace.com/wow/libstub/mainline/tags/1.0
- Libs/CallbackHandler-1.0: svn://svn.wowace.com/wow/callbackhandler/mainline/tags/1.0.3/CallbackHandler-1.0
- Libs/AceAddon-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceAddon-3.0
- Libs/AceGUI-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceGUI-3.0
- Libs/AceConfig-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceConfig-3.0
- Libs/AceConsole-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceConsole-3.0
- Libs/AceDB-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceDB-3.0
- Libs/AceDBOptions-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceDBOptions-3.0
- Libs/AceEvent-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceEvent-3.0
- Libs/AceLocale-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceLocale-3.0
- Libs/AceTimer-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceTimer-3.0
- Libs/LibSharedMedia-3.0: svn://svn.wowace.com/wow/libsharedmedia-3-0/mainline/trunk/LibSharedMedia-3.0
- Libs/AceGUI-3.0-SharedMediaWidgets: svn://svn.wowace.com/wow/ace-gui-3-0-shared-media-widgets/mainline/trunk/AceGUI-3.0-SharedMediaWidgets
- Libs/LibSink-2.0: svn://svn.wowace.com/wow/libsink-2-0/mainline/trunk/LibSink-2.0
- Libs/LibDBIcon-1.0: svn://svn.wowace.com/wow/libdbicon-1-0/mainline/trunk/LibDBIcon-1.0
-
-tools-used:
- - libdatabroker-1-1
diff --git a/Bindings.xml b/Bindings.xml
index 3490f47..9862645 100644
--- a/Bindings.xml
+++ b/Bindings.xml
@@ -1,8 +1,8 @@
-
-
- Omen:Toggle()
-
-
- Omen:ToggleFocus()
-
-
+
+
+ Omen:Toggle()
+
+
+ Omen:ToggleFocus()
+
+
diff --git a/Changelog-Omen-v3.0.9.txt b/Changelog-Omen-v3.0.9.txt
new file mode 100644
index 0000000..18a885d
--- /dev/null
+++ b/Changelog-Omen-v3.0.9.txt
@@ -0,0 +1,15 @@
+tag v3.0.9
+5edeb6f6aca65184c2b056ba77d421a91196b988
+Xinhuan
+2009-12-24 05:27:17 +0800
+
+Tag release v3.0.9
+
+
+--------------------
+
+Xinhuan:
+ - Update TOC and version.
+ - Add an option to make Omen click-through (non-interactive), defaults off.
+ - Optimized the bar animation code significantly.
+ - Fix bar backgrounds not being set to the inverted color on login if the "Invert Bar/Text Colors" setting is on. This wasn't noticable if the bar label color was the default white text with 100% alpha.
diff --git a/Libs/AceAddon-3.0/AceAddon-3.0.lua b/Libs/AceAddon-3.0/AceAddon-3.0.lua
new file mode 100644
index 0000000..3ac4eff
--- /dev/null
+++ b/Libs/AceAddon-3.0/AceAddon-3.0.lua
@@ -0,0 +1,642 @@
+--- **AceAddon-3.0** provides a template for creating addon objects.
+-- It'll provide you with a set of callback functions that allow you to simplify the loading
+-- process of your addon.\\
+-- Callbacks provided are:\\
+-- * **OnInitialize**, which is called directly after the addon is fully loaded.
+-- * **OnEnable** which gets called during the PLAYER_LOGIN event, when most of the data provided by the game is already present.
+-- * **OnDisable**, which is only called when your addon is manually being disabled.
+-- @usage
+-- -- A small (but complete) addon, that doesn't do anything,
+-- -- but shows usage of the callbacks.
+-- local MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
+--
+-- function MyAddon:OnInitialize()
+-- -- do init tasks here, like loading the Saved Variables,
+-- -- or setting up slash commands.
+-- end
+--
+-- function MyAddon:OnEnable()
+-- -- Do more initialization here, that really enables the use of your addon.
+-- -- Register Events, Hook functions, Create Frames, Get information from
+-- -- the game that wasn't available in OnInitialize
+-- end
+--
+-- function MyAddon:OnDisable()
+-- -- Unhook, Unregister Events, Hide frames that you created.
+-- -- You would probably only use an OnDisable if you want to
+-- -- build a "standby" mode, or be able to toggle modules on/off.
+-- end
+-- @class file
+-- @name AceAddon-3.0.lua
+-- @release $Id: AceAddon-3.0.lua 895 2009-12-06 16:28:55Z nevcairiel $
+
+local MAJOR, MINOR = "AceAddon-3.0", 5
+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
+
+-- Lua APIs
+local tinsert, tconcat, tremove = table.insert, table.concat, table.remove
+local fmt, tostring = string.format, tostring
+local select, pairs, next, type, unpack = select, pairs, next, type, unpack
+local loadstring, assert, error = loadstring, assert, error
+local setmetatable, getmetatable, rawset, rawget = setmetatable, getmetatable, rawset, rawget
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: LibStub, IsLoggedIn, geterrorhandler
+
+--[[
+ 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
+
+--- Create a new AceAddon-3.0 addon.
+-- Any libraries you specified will be embeded, and the addon will be scheduled for
+-- its OnInitialize and OnEnable callbacks.
+-- The final addon object, with all libraries embeded, will be returned.
+-- @paramsig [object ,]name[, lib, ...]
+-- @param object Table to use as a base for the addon (optional)
+-- @param name Name of the addon object to create
+-- @param lib List of libraries to embed into the addon
+-- @usage
+-- -- Create a simple addon object
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon", "AceEvent-3.0")
+--
+-- -- Create a Addon object based on the table of a frame
+-- local MyFrame = CreateFrame("Frame")
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon(MyFrame, "MyAddon", "AceEvent-3.0")
+function AceAddon:NewAddon(objectorname, ...)
+ local object,name
+ local i=1
+ if type(objectorname)=="table" then
+ object=objectorname
+ name=...
+ i=2
+ else
+ name=objectorname
+ end
+ if type(name)~="string" then
+ error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2)
+ end
+ if self.addons[name] then
+ error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - Addon '%s' already exists."):format(name), 2)
+ end
+
+ object = object or {}
+ object.name = name
+
+ local addonmeta = {}
+ local oldmeta = getmetatable(object)
+ if oldmeta then
+ for k, v in pairs(oldmeta) do addonmeta[k] = v end
+ end
+ addonmeta.__tostring = addontostring
+
+ setmetatable( object, addonmeta )
+ self.addons[name] = object
+ object.modules = {}
+ object.defaultModuleLibraries = {}
+ Embed( object ) -- embed NewModule, GetModule methods
+ self:EmbedLibraries(object, select(i,...))
+
+ -- add to queue of addons to be initialized upon ADDON_LOADED
+ tinsert(self.initializequeue, object)
+ return object
+end
+
+
+--- Get the addon object by its name from the internal AceAddon registry.
+-- Throws an error if the addon object cannot be found (except if silent is set).
+-- @param name unique name of the addon object
+-- @param silent if true, the addon is optional, silently return nil if its not found
+-- @usage
+-- -- Get the Addon
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+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
+
+-- - Embed a list of libraries into the specified addon.
+-- This function will try to embed all of the listed libraries into the addon
+-- and error if a single one fails.
+--
+-- **Note:** This function is for internal use by :NewAddon/:NewModule
+-- @paramsig addon, [lib, ...]
+-- @param addon addon object to embed the libs in
+-- @param lib List of libraries to embed into the addon
+function AceAddon:EmbedLibraries(addon, ...)
+ for i=1,select("#", ... ) do
+ local libname = select(i, ...)
+ self:EmbedLibrary(addon, libname, false, 4)
+ end
+end
+
+-- - Embed a library into the addon object.
+-- This function will check if the specified library is registered with LibStub
+-- and if it has a :Embed function to call. It'll error if any of those conditions
+-- fails.
+--
+-- **Note:** This function is for internal use by :EmbedLibraries
+-- @paramsig addon, libname[, silent[, offset]]
+-- @param addon addon object to embed the library in
+-- @param libname name of the library to embed
+-- @param silent marks an embed to fail silently if the library doesn't exist (optional)
+-- @param offset will push the error messages back to said offset, defaults to 2 (optional)
+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
+
+--- Return the specified module from an addon object.
+-- Throws an error if the addon object cannot be found (except if silent is set)
+-- @name //addon//:GetModule
+-- @paramsig name[, silent]
+-- @param name unique name of the module
+-- @param silent if true, the module is optional, silently return nil if its not found (optional)
+-- @usage
+-- -- Get the Addon
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- -- Get the Module
+-- MyModule = MyAddon:GetModule("MyModule")
+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
+
+--- Create a new module for the addon.
+-- The new module can have its own embeded libraries and/or use a module prototype to be mixed into the module.\\
+-- A module has the same functionality as a real addon, it can have modules of its own, and has the same API as
+-- an addon object.
+-- @name //addon//:NewModule
+-- @paramsig name[, prototype|lib[, lib, ...]]
+-- @param name unique name of the module
+-- @param prototype object to derive this module from, methods and values from this table will be mixed into the module (optional)
+-- @param lib List of libraries to embed into the addon
+-- @usage
+-- -- Create a module with some embeded libraries
+-- MyModule = MyAddon:NewModule("MyModule", "AceEvent-3.0", "AceHook-3.0")
+--
+-- -- Create a module with a prototype
+-- local prototype = { OnEnable = function(self) print("OnEnable called!") end }
+-- MyModule = MyAddon:NewModule("MyModule", prototype, "AceEvent-3.0", "AceHook-3.0")
+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
+
+--- Returns the real name of the addon or module, without any prefix.
+-- @name //addon//:GetName
+-- @paramsig
+-- @usage
+-- print(MyAddon:GetName())
+-- -- prints "MyAddon"
+function GetName(self)
+ return self.moduleName or self.name
+end
+
+--- Enables the Addon, if possible, return true or false depending on success.
+-- This internally calls AceAddon:EnableAddon(), thus dispatching a OnEnable callback
+-- and enabling all modules of the addon (unless explicitly disabled).\\
+-- :Enable() also sets the internal `enableState` variable to true
+-- @name //addon//:Enable
+-- @paramsig
+-- @usage
+-- -- Enable MyModule
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyModule = MyAddon:GetModule("MyModule")
+-- MyModule:Enable()
+function Enable(self)
+ self:SetEnabledState(true)
+ return AceAddon:EnableAddon(self)
+end
+
+--- Disables the Addon, if possible, return true or false depending on success.
+-- This internally calls AceAddon:DisableAddon(), thus dispatching a OnDisable callback
+-- and disabling all modules of the addon.\\
+-- :Disable() also sets the internal `enableState` variable to false
+-- @name //addon//:Disable
+-- @paramsig
+-- @usage
+-- -- Disable MyAddon
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyAddon:Disable()
+function Disable(self)
+ self:SetEnabledState(false)
+ return AceAddon:DisableAddon(self)
+end
+
+--- Enables the Module, if possible, return true or false depending on success.
+-- Short-hand function that retrieves the module via `:GetModule` and calls `:Enable` on the module object.
+-- @name //addon//:EnableModule
+-- @paramsig name
+-- @usage
+-- -- Enable MyModule using :GetModule
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyModule = MyAddon:GetModule("MyModule")
+-- MyModule:Enable()
+--
+-- -- Enable MyModule using the short-hand
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyAddon:EnableModule("MyModule")
+function EnableModule(self, name)
+ local module = self:GetModule( name )
+ return module:Enable()
+end
+
+--- Disables the Module, if possible, return true or false depending on success.
+-- Short-hand function that retrieves the module via `:GetModule` and calls `:Disable` on the module object.
+-- @name //addon//:DisableModule
+-- @paramsig name
+-- @usage
+-- -- Disable MyModule using :GetModule
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyModule = MyAddon:GetModule("MyModule")
+-- MyModule:Disable()
+--
+-- -- Disable MyModule using the short-hand
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyAddon:DisableModule("MyModule")
+function DisableModule(self, name)
+ local module = self:GetModule( name )
+ return module:Disable()
+end
+
+--- Set the default libraries to be mixed into all modules created by this object.
+-- Note that you can only change the default module libraries before any module is created.
+-- @name //addon//:SetDefaultModuleLibraries
+-- @paramsig lib[, lib, ...]
+-- @param lib List of libraries to embed into the addon
+-- @usage
+-- -- Create the addon object
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
+-- -- Configure default libraries for modules (all modules need AceEvent-3.0)
+-- MyAddon:SetDefaultModuleLibraries("AceEvent-3.0")
+-- -- Create a module
+-- MyModule = MyAddon:NewModule("MyModule")
+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
+
+--- Set the default state in which new modules are being created.
+-- Note that you can only change the default state before any module is created.
+-- @name //addon//:SetDefaultModuleState
+-- @paramsig state
+-- @param state Default state for new modules, true for enabled, false for disabled
+-- @usage
+-- -- Create the addon object
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
+-- -- Set the default state to "disabled"
+-- MyAddon:SetDefaultModuleState(false)
+-- -- Create a module and explicilty enable it
+-- MyModule = MyAddon:NewModule("MyModule")
+-- MyModule:Enable()
+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
+
+--- Set the default prototype to use for new modules on creation.
+-- Note that you can only change the default prototype before any module is created.
+-- @name //addon//:SetDefaultModulePrototype
+-- @paramsig prototype
+-- @param prototype Default prototype for the new modules (table)
+-- @usage
+-- -- Define a prototype
+-- local prototype = { OnEnable = function(self) print("OnEnable called!") end }
+-- -- Set the default prototype
+-- MyAddon:SetDefaultModulePrototype(prototype)
+-- -- Create a module and explicitly Enable it
+-- MyModule = MyAddon:NewModule("MyModule")
+-- MyModule:Enable()
+-- -- should print "OnEnable called!" now
+-- @see NewModule
+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
+
+--- Set the state of an addon or module
+-- This should only be called before any enabling actually happend, e.g. in/before OnInitialize.
+-- @name //addon//:SetEnabledState
+-- @paramsig state
+-- @param state the state of an addon or module (enabled=true, disabled=false)
+function SetEnabledState(self, state)
+ self.enabledState = state
+end
+
+
+--- Return an iterator of all modules associated to the addon.
+-- @name //addon//:IterateModules
+-- @paramsig
+-- @usage
+-- -- Enable all modules
+-- for name, module in MyAddon:IterateModules() do
+-- module:Enable()
+-- end
+local function IterateModules(self) return pairs(self.modules) end
+
+-- Returns an iterator of all embeds in the addon
+-- @name //addon//:IterateEmbeds
+-- @paramsig
+local function IterateEmbeds(self) return pairs(AceAddon.embeds[self]) end
+
+--- Query the enabledState of an addon.
+-- @name //addon//:IsEnabled
+-- @paramsig
+-- @usage
+-- if MyAddon:IsEnabled() then
+-- MyAddon:Disable()
+-- 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
+
+
+-- - Initialize the addon after creation.
+-- This function is only used internally during the ADDON_LOADED event
+-- It will call the **OnInitialize** function on the addon object (if present),
+-- and the **OnEmbedInitialize** function on all embeded libraries.
+--
+-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
+-- @param addon addon object to intialize
+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
+
+-- - Enable the addon after creation.
+-- Note: This function is only used internally during the PLAYER_LOGIN event, or during ADDON_LOADED,
+-- if IsLoggedIn() already returns true at that point, e.g. for LoD Addons.
+-- It will call the **OnEnable** function on the addon object (if present),
+-- and the **OnEmbedEnable** function on all embeded libraries.\\
+-- This function does not toggle the enable state of the addon itself, and will return early if the addon is disabled.
+--
+-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
+-- Use :Enable on the addon itself instead.
+-- @param addon addon object to enable
+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
+
+ 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
+
+-- - Disable the addon
+-- Note: This function is only used internally.
+-- It will call the **OnDisable** function on the addon object (if present),
+-- and the **OnEmbedDisable** function on all embeded libraries.\\
+-- This function does not toggle the enable state of the addon itself, and will return early if the addon is still enabled.
+--
+-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
+-- Use :Disable on the addon itself instead.
+-- @param addon addon object to enable
+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
+
+--- Get an iterator over all registered addons.
+-- @usage
+-- -- Print a list of all installed AceAddon's
+-- for name, addon in AceAddon:IterateAddons() do
+-- print("Addon: " .. name)
+-- end
+function AceAddon:IterateAddons() return pairs(self.addons) end
+
+--- Get an iterator over the internal status registry.
+-- @usage
+-- -- Print a list of all enabled addons
+-- for name, status in AceAddon:IterateAddonStatus() do
+-- if status then
+-- print("EnabledAddon: " .. name)
+-- end
+-- end
+function AceAddon:IterateAddonStatus() return pairs(self.statuses) end
+
+-- Following Iterators are deprecated, and their addon specific versions should be used
+-- e.g. addon:IterateEmbeds() instead of :IterateEmbedsOnAddon(addon)
+function AceAddon:IterateEmbedsOnAddon(addon) return pairs(self.embeds[addon]) 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
diff --git a/Libs/AceAddon-3.0/AceAddon-3.0.xml b/Libs/AceAddon-3.0/AceAddon-3.0.xml
new file mode 100644
index 0000000..17c568c
--- /dev/null
+++ b/Libs/AceAddon-3.0/AceAddon-3.0.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/Libs/AceConfig-3.0/AceConfig-3.0.lua b/Libs/AceConfig-3.0/AceConfig-3.0.lua
new file mode 100644
index 0000000..b66022d
--- /dev/null
+++ b/Libs/AceConfig-3.0/AceConfig-3.0.lua
@@ -0,0 +1,57 @@
+--- AceConfig-3.0 wrapper library.
+-- Provides an API to register an options table with the config registry,
+-- as well as associate it with a slash command.
+-- @class file
+-- @name AceConfig-3.0
+-- @release $Id: AceConfig-3.0.lua 877 2009-11-02 15:56:50Z nevcairiel $
+
+--[[
+AceConfig-3.0
+
+Very light wrapper library that combines all the AceConfig subcomponents into one more easily used whole.
+
+]]
+
+local MAJOR, MINOR = "AceConfig-3.0", 2
+local AceConfig = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceConfig 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")
+
+-- Lua APIs
+local pcall, error, type, pairs = pcall, error, type, pairs
+
+-- -------------------------------------------------------------------
+-- :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
+
+--- Register a option table with the AceConfig registry.
+-- You can supply a slash command (or a table of slash commands) to register with AceConfigCmd directly.
+-- @paramsig appName, options [, slashcmd]
+-- @param appName The application name for the config table.
+-- @param options The option table (or a function to generate one on demand)
+-- @param slashcmd A slash command to register for the option table, or a table of slash commands.
+-- @usage
+-- local AceConfig = LibStub("AceConfig-3.0")
+-- AceConfig:RegisterOptionsTable("MyAddon", myOptions, {"/myslash", "/my"})
+function AceConfig: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
diff --git a/Libs/AceConfig-3.0/AceConfig-3.0.xml b/Libs/AceConfig-3.0/AceConfig-3.0.xml
new file mode 100644
index 0000000..d9eb96a
--- /dev/null
+++ b/Libs/AceConfig-3.0/AceConfig-3.0.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Libs/AceConfig-3.0/AceConfigCmd-3.0/AceConfigCmd-3.0.lua b/Libs/AceConfig-3.0/AceConfigCmd-3.0/AceConfigCmd-3.0.lua
new file mode 100644
index 0000000..3936031
--- /dev/null
+++ b/Libs/AceConfig-3.0/AceConfigCmd-3.0/AceConfigCmd-3.0.lua
@@ -0,0 +1,787 @@
+--- AceConfigCmd-3.0 handles access to an options table through the "command line" interface via the ChatFrames.
+-- @class file
+-- @name AceConfigCmd-3.0
+-- @release $Id: AceConfigCmd-3.0.lua 904 2009-12-13 11:56:37Z nevcairiel $
+
+--[[
+AceConfigCmd-3.0
+
+Handles commandline optionstable access
+
+REQUIRES: AceConsole-3.0 for command registration (loaded on demand)
+
+]]
+
+-- TODO: plugin args
+
+
+local MAJOR, MINOR = "AceConfigCmd-3.0", 12
+local AceConfigCmd = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceConfigCmd then return end
+
+AceConfigCmd.commands = AceConfigCmd.commands or {}
+local commands = AceConfigCmd.commands
+
+local cfgreg = LibStub("AceConfigRegistry-3.0")
+local AceConsole -- LoD
+local AceConsoleName = "AceConsole-3.0"
+
+-- Lua APIs
+local strsub, strsplit, strlower, strmatch, strtrim = string.sub, string.split, string.lower, string.match, string.trim
+local format, tonumber, tostring = string.format, tonumber, tostring
+local tsort, tinsert = table.sort, table.insert
+local select, pairs, next, type = select, pairs, next, type
+local error, assert = error, assert
+
+-- WoW APIs
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: LibStub, SELECTED_CHAT_FRAME, DEFAULT_CHAT_FRAME
+
+
+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
+
+-- 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"
+
+
+-- 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 checkhidden(info, inputpos, tab)
+ if tab.cmdHidden~=nil then
+ return tab.cmdHidden
+ end
+ local hidden = tab.hidden
+ if type(hidden) == "function" or type(hidden) == "string" then
+ info.hidden = hidden
+ hidden = callmethod(info, inputpos, tab, 'hidden')
+ info.hidden = nil
+ end
+ return hidden
+end
+
+local function showhelp(info, inputpos, tab, depth, 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
+ tinsert(sortTbl, k)
+ refTbl[k] = v
+ end
+ end
+
+ tsort(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 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
+
+-- 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 = (info.input):find(" *([^ ]+) *", inputpos)
+ if not arg then
+ showhelp(info, inputpos, tab, depth)
+ 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:gsub(" ", "_")) 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 valinfo.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))
+
+ 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
+
+ if str == "" then
+ local b = callmethod(info, inputpos, tab, "get")
+ local fmt = "|cffffff78- [%s]|r %s"
+ local fmt_sel = "|cffffff78- [%s]|r %s |cffff0000*|r"
+ print(L["Options for |cffffff78"..info[#info].."|r:"])
+ for k, v in pairs(values) do
+ if b == k then
+ print(fmt_sel:format(k, v))
+ else
+ print(fmt:format(k, v))
+ end
+ end
+ return
+ 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))
+
+ 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
+
+ if str == "" then
+ local fmt = "|cffffff78- [%s]|r %s"
+ local fmt_sel = "|cffffff78- [%s]|r %s |cffff0000*|r"
+ print(L["Options for |cffffff78"..info[#info].."|r (multiple possible):"])
+ for k, v in pairs(values) do
+ if callmethod(info, inputpos, tab, "get", k) then
+ print(fmt_sel:format(k, v))
+ else
+ print(fmt:format(k, v))
+ end
+ end
+ return
+ 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 str:gmatch("[^ ]+") do
+ --parse option=on etc
+ local opt, val = v:match('(.+)=(.+)')
+ --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)
+
+ elseif tab.type=="description" then
+ ------------ description --------------------
+ -- ignore description, GUI config only
+ else
+ err(info, inputpos, "unknown options table item type '"..tostring(tab.type).."'")
+ end
+end
+
+--- Handle the chat command.
+-- This is usually called from a chat command handler to parse the command input as operations on an aceoptions table.\\
+-- AceConfigCmd uses this function internally when a slash command is registered with `:CreateChatCommand`
+-- @param slashcmd The slash command WITHOUT leading slash (only used for error output)
+-- @param appName The application name as given to `:RegisterOptionsTable()`
+-- @param input The commandline input (as given by the WoW handler, i.e. without the command itself)
+-- @usage
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon", "AceConsole-3.0")
+-- -- Use AceConsole-3.0 to register a Chat Command
+-- MyAddon:RegisterChatCommand("mychat", "ChatCommand")
+--
+-- -- Show the GUI if no input is supplied, otherwise handle the chat input.
+-- function MyAddon:ChatCommand(input)
+-- -- Assuming "MyOptions" is the appName of a valid options table
+-- if not input or input:trim() == "" then
+-- LibStub("AceConfigDialog-3.0"):Open("MyOptions")
+-- else
+-- LibStub("AceConfigCmd-3.0").HandleCommand(MyAddon, "mychat", "MyOptions", input)
+-- end
+-- end
+function AceConfigCmd: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
+
+--- Utility function to create a slash command handler.
+-- Also registers tab completion with AceTab
+-- @param slashcmd The slash command WITHOUT leading slash (only used for error output)
+-- @param appName The application name as given to `:RegisterOptionsTable()`
+function AceConfigCmd:CreateChatCommand(slashcmd, appName)
+ if not AceConsole then
+ AceConsole = LibStub(AceConsoleName)
+ end
+ if AceConsole.RegisterChatCommand(self, slashcmd, function(input)
+ AceConfigCmd.HandleCommand(self, slashcmd, appName, input) -- upgradable
+ end,
+ true) then -- succesfully registered so lets get the command -> app table in
+ commands[slashcmd] = appName
+ end
+end
+
+--- Utility function that returns the options table that belongs to a slashcommand.
+-- Designed to be used for the AceTab interface.
+-- @param slashcmd The slash command WITHOUT leading slash (only used for error output)
+-- @return The options table associated with the slash command (or nil if the slash command was not registered)
+function AceConfigCmd:GetChatCommandOptions(slashcmd)
+ return commands[slashcmd]
+end
diff --git a/Libs/AceConfig-3.0/AceConfigCmd-3.0/AceConfigCmd-3.0.xml b/Libs/AceConfig-3.0/AceConfigCmd-3.0/AceConfigCmd-3.0.xml
new file mode 100644
index 0000000..b2f2c20
--- /dev/null
+++ b/Libs/AceConfig-3.0/AceConfigCmd-3.0/AceConfigCmd-3.0.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/Libs/AceConfig-3.0/AceConfigDialog-3.0/AceConfigDialog-3.0.lua b/Libs/AceConfig-3.0/AceConfigDialog-3.0/AceConfigDialog-3.0.lua
new file mode 100644
index 0000000..bc706ef
--- /dev/null
+++ b/Libs/AceConfig-3.0/AceConfigDialog-3.0/AceConfigDialog-3.0.lua
@@ -0,0 +1,1895 @@
+--- AceConfigDialog-3.0 generates AceGUI-3.0 based windows based on option tables.
+-- @class file
+-- @name AceConfigDialog-3.0
+-- @release $Id: AceConfigDialog-3.0.lua 902 2009-12-12 14:56:14Z nevcairiel $
+
+local LibStub = LibStub
+local MAJOR, MINOR = "AceConfigDialog-3.0", 43
+local AceConfigDialog, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceConfigDialog then return end
+
+AceConfigDialog.OpenFrames = AceConfigDialog.OpenFrames or {}
+AceConfigDialog.Status = AceConfigDialog.Status or {}
+AceConfigDialog.frame = AceConfigDialog.frame or CreateFrame("Frame")
+
+AceConfigDialog.frame.apps = AceConfigDialog.frame.apps or {}
+AceConfigDialog.frame.closing = AceConfigDialog.frame.closing or {}
+
+local gui = LibStub("AceGUI-3.0")
+local reg = LibStub("AceConfigRegistry-3.0")
+
+-- Lua APIs
+local tconcat, tinsert, tsort, tremove = table.concat, table.insert, table.sort, table.remove
+local strmatch, format = string.match, string.format
+local assert, loadstring, error = assert, loadstring, error
+local pairs, next, select, type, unpack = pairs, next, select, type, unpack
+local rawset, tostring = rawset, tostring
+local math_min, math_max, math_floor = math.min, math.max, math.floor
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: NORMAL_FONT_COLOR, GameTooltip, StaticPopupDialogs, ACCEPT, CANCEL, StaticPopup_Show
+-- GLOBALS: PlaySound, GameFontHighlight, GameFontHighlightSmall, GameFontHighlightLarge
+-- GLOBALS: CloseSpecialWindows, InterfaceOptions_AddCategory, geterrorhandler
+
+local emptyTbl = {}
+
+--[[
+ 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, ...)
+ 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,
+ fontSize = true,
+}
+
+--Is Never a function or method
+local allIsLiteral = {
+ type = true,
+ descStyle = 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.appName = appName
+ 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(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
+
+ tsort(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:GetUserDataTable()
+
+ if user.path then
+ del(user.path)
+ end
+
+ if widget.type == "TreeGroup" then
+ local tree = user.tree
+ widget:SetTree(nil)
+ if tree then
+ for i = 1, #tree do
+ DelTree(tree[i])
+ del(tree[i])
+ end
+ del(tree)
+ end
+ end
+
+ if widget.type == "TabGroup" then
+ widget:SetTabs(nil)
+ if user.tablist then
+ del(user.tablist)
+ end
+ end
+
+ if widget.type == "DropdownGroup" then
+ widget:SetGroupList(nil)
+ if user.grouplist then
+ del(user.grouplist)
+ end
+ end
+end
+
+-- - Gets a status table for the given appname and options path.
+-- @param appName The application name as given to `:RegisterOptionsTable()`
+-- @param path The path to the options (a table with all group keys)
+-- @return
+function AceConfigDialog: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
+
+--- Selects the specified path in the options window.
+-- The path specified has to match the keys of the groups in the table.
+-- @param appName The application name as given to `:RegisterOptionsTable()`
+-- @param ... The path to the key that should be selected
+function AceConfigDialog:SelectGroup(appName, ...)
+ local path = new()
+
+
+ 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 group = options
+ local status = self:GetStatusTable(appName, path)
+ if not status.groups then
+ status.groups = {}
+ end
+ status = status.groups
+ local treevalue
+ local treestatus
+
+ for n = 1, select('#',...) do
+ local key = select(n, ...)
+
+ if group.childGroups == "tab" or group.childGroups == "select" then
+ --if this is a tab or select group, select the group
+ status.selected = key
+ --children of this group are no longer extra levels of a tree
+ treevalue = nil
+ else
+ --tree group by default
+ if treevalue then
+ --this is an extra level of a tree group, build a uniquevalue for it
+ treevalue = treevalue.."\001"..key
+ else
+ --this is the top level of a tree group, the uniquevalue is the same as the key
+ treevalue = key
+ if not status.groups then
+ status.groups = {}
+ end
+ --save this trees status table for any extra levels or groups
+ treestatus = status
+ end
+ --make sure that the tree entry is open, and select it.
+ --the selected group will be overwritten if a child is the final target but still needs to be open
+ treestatus.selected = treevalue
+ treestatus.groups[treevalue] = true
+
+ end
+
+ --move to the next group in the path
+ group = GetSubOption(group, key)
+ if not group then
+ break
+ end
+ tinsert(path, key)
+ status = self:GetStatusTable(appName, path)
+ if not status.groups then
+ status.groups = {}
+ end
+ status = status.groups
+ end
+
+ del(path)
+ reg:NotifyChange(appName)
+end
+
+local function OptionOnMouseOver(widget, event)
+ --show a tooltip/set the status bar to the desc text
+ local user = widget:GetUserDataTable()
+ 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)
+ local descStyle = opt.descStyle
+
+ if descStyle and descStyle ~= "tooltip" then return end
+
+ 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, basepath, 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
+ AceConfigDialog:Open(appName, rootframe, unpack(basepath or emptyTbl))
+ del(info)
+ end
+ t.OnCancel = function()
+ if dialog and oldstrata then
+ dialog:SetFrameStrata(oldstrata)
+ end
+ AceConfigDialog:Open(appName, rootframe, unpack(basepath or emptyTbl))
+ 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:GetUserDataTable() contains the needed info
+ local user = widget:GetUserDataTable()
+ 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.appName = 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(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)
+ else
+ -- TODO: do something else.
+ 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
+ else
+ -- TODO: do something else
+ 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(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:GetUserData('iscustom')
+ local rootframe
+
+ if iscustom then
+ rootframe = user.rootframe
+ end
+ local basepath = user.rootframe:GetUserData('basepath')
+ if type(func) == "string" then
+ if handler and handler[func] then
+ confirmPopup(user.appName, rootframe, basepath, info, confirmText, handler[func], handler, info, ...)
+ else
+ error(format("Method %s doesn't exist in handler for type func", func))
+ end
+ elseif type(func) == "function" then
+ confirmPopup(user.appName, rootframe, basepath, 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(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:GetUserData('iscustom')
+ local basepath = user.rootframe:GetUserData('basepath') or emptyTbl
+ --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
+ AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath))
+ else
+ AceConfigDialog:Open(user.appName, unpack(basepath))
+ end
+ end
+ elseif option.type == "range" then
+ if event == "OnMouseUp" then
+ if iscustom then
+ AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath))
+ else
+ AceConfigDialog:Open(user.appName, unpack(basepath))
+ end
+ end
+ --multiselects don't cause a refresh on 'OnValueChanged' only 'OnClosed'
+ elseif option.type == "multiselect" then
+ user.valuechanged = true
+ else
+ if iscustom then
+ AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath))
+ else
+ AceConfigDialog:Open(user.appName, unpack(basepath))
+ end
+ end
+
+ end
+ del(info)
+end
+
+local function ActivateSlider(widget, event, value)
+ local option = widget:GetUserData('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
+
+--called from a checkbox that is part of an internally created multiselect group
+--this type is safe to refresh on activation of one control
+local function ActivateMultiControl(widget, event, ...)
+ ActivateControl(widget, event, widget:GetUserData('value'), ...)
+ local user = widget:GetUserDataTable()
+ local iscustom = user.rootframe:GetUserData('iscustom')
+ local basepath = user.rootframe:GetUserData('basepath') or emptyTbl
+ if iscustom then
+ AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath))
+ else
+ AceConfigDialog:Open(user.appName, unpack(basepath))
+ end
+end
+
+local function MultiControlOnClosed(widget, event, ...)
+ local user = widget:GetUserDataTable()
+ if user.valuechanged then
+ local iscustom = user.rootframe:GetUserData('iscustom')
+ local basepath = user.rootframe:GetUserData('basepath') or emptyTbl
+ if iscustom then
+ AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath))
+ else
+ AceConfigDialog:Open(user.appName, unpack(basepath))
+ end
+ end
+end
+
+local function FrameOnClose(widget, event)
+ local appName = widget:GetUserData('appName')
+ AceConfigDialog.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.icon = GetOptionsMemberValue("icon", 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.icon = GetOptionsMemberValue("icon", 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:GetUserDataTable()
+ 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
+
+ local imageCoords = GetOptionsMemberValue("imageCoords",v, options, path, appName)
+ local image, width, height = GetOptionsMemberValue("image",v, options, path, appName)
+
+ if type(image) == 'string' then
+ control = gui:Create("Icon")
+ 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)
+ control:SetLabel(name)
+ else
+ control = gui:Create("Button")
+ control:SetText(name)
+ end
+ 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
+ geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType)))
+ control = gui:Create(v.multiline and "MultiLineEditBox" or "EditBox")
+ 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)
+
+ if v.descStyle == "inline" then
+ local desc = GetOptionsMemberValue("desc", v, options, path, appName)
+ control:SetDescription(desc)
+ end
+
+ local image = GetOptionsMemberValue("image", v, options, path, appName)
+ local imageCoords = GetOptionsMemberValue("imageCoords", v, options, path, appName)
+
+ if type(image) == 'string' then
+ if type(imageCoords) == 'table' then
+ control:SetImage(image, unpack(imageCoords))
+ else
+ control:SetImage(image)
+ end
+ end
+ 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
+ geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType)))
+ control = gui:Create("Dropdown")
+ 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)
+ local disabled = CheckOptionDisabled(v, options, path, appName)
+
+ local controlType = v.dialogControl or v.control
+
+ local valuesort = new()
+ if values then
+ for value, text in pairs(values) do
+ tinsert(valuesort, value)
+ end
+ end
+ tsort(valuesort)
+
+ if controlType then
+ control = gui:Create(controlType)
+ if not control then
+ geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType)))
+ end
+ end
+ if control then
+ control:SetMultiselect(true)
+ control:SetLabel(name)
+ control:SetList(values)
+ control:SetDisabled(disabled)
+ control:SetCallback("OnValueChanged",ActivateControl)
+ control:SetCallback("OnClosed", MultiControlOnClosed)
+ 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
+ --check:SetTriState(v.tristate)
+ for i = 1, #valuesort do
+ local key = valuesort[i]
+ local value = GetOptionsMemberValue("get",v, options, path, appName, key)
+ control:SetItemValue(key,value)
+ end
+ else
+ control = gui:Create("InlineGroup")
+ control:SetLayout("Flow")
+ control:SetTitle(name)
+ control.width = "fill"
+
+ 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:SetUserData('value', value)
+ check:SetUserData('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 fontSize = GetOptionsMemberValue("fontSize",v, options, path, appName)
+ if fontSize == "medium" then
+ control:SetFontObject(GameFontHighlight)
+ elseif fontSize == "large" then
+ control:SetFontObject(GameFontHighlightLarge)
+ else -- small or invalid
+ control:SetFontObject(GameFontHighlightSmall)
+ end
+
+ 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 TreeOnButtonEnter(widget, event, uniquevalue, button)
+ local user = widget:GetUserDataTable()
+ if not user then return end
+ local options = user.options
+ local option = user.option
+ local path = user.path
+ local appName = user.appName
+
+ local feedpath = new()
+ for i = 1, #path do
+ feedpath[i] = path[i]
+ end
+
+ BuildPath(feedpath, ("\001"):split(uniquevalue))
+ local group = options
+ for i = 1, #feedpath do
+ if not group then return end
+ group = GetSubOption(group, feedpath[i])
+ end
+
+ local name = GetOptionsMemberValue("name", group, options, feedpath, appName)
+ local desc = GetOptionsMemberValue("desc", group, options, feedpath, appName)
+
+ GameTooltip:SetOwner(button, "ANCHOR_NONE")
+ if widget.type == "TabGroup" then
+ GameTooltip:SetPoint("BOTTOM",button,"TOP")
+ else
+ GameTooltip:SetPoint("LEFT",button,"RIGHT")
+ end
+
+ GameTooltip:SetText(name, 1, .82, 0, 1)
+
+ if type(desc) == "string" then
+ GameTooltip:AddLine(desc, 1, 1, 1, 1)
+ end
+
+ GameTooltip:Show()
+end
+
+local function TreeOnButtonLeave(widget, event, value, button)
+ GameTooltip:Hide()
+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, ("\001"):split(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:GetUserDataTable()
+
+ 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, ("\001"):split(uniquevalue))
+ local group = options
+ for i = 1, #feedpath do
+ group = GetSubOption(group, feedpath[i])
+ end
+ widget:ReleaseChildren()
+ AceConfigDialog:FeedGroup(user.appName,options,widget,rootframe,feedpath)
+
+ del(feedpath)
+end
+
+
+
+--[[
+-- INTERNAL --
+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 AceConfigDialog:FeedGroup(appName,options,container,rootframe,path, isRoot)
+ 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) and not CheckOptionHidden(v, options, path, appName) 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) and not CheckOptionHidden(v, options, path, appName) 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" and not isRoot)) then
+ if container.type ~= "InlineGroup" and container.type ~= "SimpleGroup" 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
+ local name = GetOptionsMemberValue("name", group, options, path, appName)
+ if grouptype == "tab" then
+
+ local tab = gui:Create("TabGroup")
+ InjectInfo(tab, options, group, path, rootframe, appName)
+ tab:SetCallback("OnGroupSelected", GroupSelected)
+ tab:SetCallback("OnTabEnter", TreeOnButtonEnter)
+ tab:SetCallback("OnTabLeave", TreeOnButtonLeave)
+
+ local status = AceConfigDialog: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)
+ tab:SetUserData("tablist", 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")
+ select:SetTitle(name)
+ InjectInfo(select, options, group, path, rootframe, appName)
+ select:SetCallback("OnGroupSelected", GroupSelected)
+ local status = AceConfigDialog: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)
+ select:SetUserData("grouplist", 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.selected) and status.groups.selected) 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") or isRoot then
+ local tree = gui:Create("TreeGroup")
+ InjectInfo(tree, options, group, path, rootframe, appName)
+ tree:EnableButtonTooltips(false)
+
+ tree.width = "fill"
+ tree.height = "fill"
+
+ tree:SetCallback("OnGroupSelected", GroupSelected)
+ tree:SetCallback("OnButtonEnter", TreeOnButtonEnter)
+ tree:SetCallback("OnButtonLeave", TreeOnButtonLeave)
+
+ local status = AceConfigDialog: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)
+ tree:SetUserData("tree",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
+
+
+local function RefreshOnUpdate(this)
+ for appName in pairs(this.closing) do
+ if AceConfigDialog.OpenFrames[appName] then
+ AceConfigDialog.OpenFrames[appName]:Hide()
+ end
+ if AceConfigDialog.BlizOptions and AceConfigDialog.BlizOptions[appName] then
+ for key, widget in pairs(AceConfigDialog.BlizOptions[appName]) do
+ if not widget:IsVisible() then
+ widget:ReleaseChildren()
+ end
+ end
+ end
+ this.closing[appName] = nil
+ end
+
+ if this.closeAll then
+ for k, v in pairs(AceConfigDialog.OpenFrames) do
+ v:Hide()
+ end
+ this.closeAll = nil
+ end
+
+ for appName in pairs(this.apps) do
+ if AceConfigDialog.OpenFrames[appName] then
+ local user = AceConfigDialog.OpenFrames[appName]:GetUserDataTable()
+ AceConfigDialog:Open(appName, unpack(user.basepath or emptyTbl))
+ end
+ if AceConfigDialog.BlizOptions and AceConfigDialog.BlizOptions[appName] then
+ for key, widget in pairs(AceConfigDialog.BlizOptions[appName]) do
+ local user = widget:GetUserDataTable()
+ if widget:IsVisible() then
+ AceConfigDialog:Open(widget:GetUserData('appName'), widget, unpack(user.basepath or emptyTbl))
+ end
+ end
+ end
+ this.apps[appName] = nil
+ end
+ this:SetScript("OnUpdate", nil)
+end
+
+-- Upgrade the OnUpdate script as well, if needed.
+if AceConfigDialog.frame:GetScript("OnUpdate") then
+ AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate)
+end
+
+--- Close all open options windows
+function AceConfigDialog:CloseAll()
+ AceConfigDialog.frame.closeAll = true
+ AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate)
+ if next(self.OpenFrames) then
+ return true
+ end
+end
+
+--- Close a specific options window.
+-- @param appName The application name as given to `:RegisterOptionsTable()`
+function AceConfigDialog:Close(appName)
+ if self.OpenFrames[appName] then
+ AceConfigDialog.frame.closing[appName] = true
+ AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate)
+ return true
+ end
+end
+
+-- Internal -- Called by AceConfigRegistry
+function AceConfigDialog:ConfigTableChanged(event, appName)
+ AceConfigDialog.frame.apps[appName] = true
+ AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate)
+end
+
+reg.RegisterCallback(AceConfigDialog, "ConfigTableChange", "ConfigTableChanged")
+
+--- Sets the default size of the options window for a specific application.
+-- @param appName The application name as given to `:RegisterOptionsTable()`
+-- @param width The default width
+-- @param height The default height
+function AceConfigDialog:SetDefaultSize(appName, width, height)
+ local status = AceConfigDialog:GetStatusTable(appName)
+ if type(width) == "number" and type(height) == "number" then
+ status.width = width
+ status.height = height
+ end
+end
+
+--- Open an option window at the specified path (if any).
+-- This function can optionally feed the group into a pre-created container
+-- instead of creating a new container frame.
+-- @paramsig appName [, container][, ...]
+-- @param appName The application name as given to `:RegisterOptionsTable()`
+-- @param container An optional container frame to feed the options into
+-- @param ... The path to open after creating the options window (see `:SelectGroup` for details)
+function AceConfigDialog: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 an optional path is specified add it to the path table before feeding the options
+ --as container is optional as well it may contain the first element of the path
+ if type(container) == "string" then
+ tinsert(path, container)
+ container = nil
+ end
+ for n = 1, select('#',...) do
+ tinsert(path, (select(n, ...)))
+ end
+
+ --if a container is given feed into that
+ if container then
+ f = container
+ f:ReleaseChildren()
+ f:SetUserData('appName', appName)
+ f:SetUserData('iscustom', true)
+ if #path > 0 then
+ f:SetUserData('basepath', copy(path))
+ end
+ local status = AceConfigDialog: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
+ if f.SetTitle then
+ f:SetTitle(name or "")
+ 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:SetUserData('appName', appName)
+ if #path > 0 then
+ f:SetUserData('basepath', copy(path))
+ end
+ f:SetTitle(name or "")
+ local status = AceConfigDialog:GetStatusTable(appName)
+ f:SetStatusTable(status)
+ end
+
+ self:FeedGroup(appName,options,f,f,path,true)
+ if f.Show then
+ f:Show()
+ end
+ del(path)
+end
+
+-- convert pre-39 BlizOptions structure to the new format
+if oldminor and oldminor < 39 and AceConfigDialog.BlizOptions then
+ local old = AceConfigDialog.BlizOptions
+ local new = {}
+ for key, widget in pairs(old) do
+ local appName = widget:GetUserData('appName')
+ if not new[appName] then new[appName] = {} end
+ new[appName][key] = widget
+ end
+ AceConfigDialog.BlizOptions = new
+else
+ AceConfigDialog.BlizOptions = AceConfigDialog.BlizOptions or {}
+end
+
+local function FeedToBlizPanel(widget, event)
+ local path = widget:GetUserData('path')
+ AceConfigDialog:Open(widget:GetUserData('appName'), widget, unpack(path or emptyTbl))
+end
+
+local function ClearBlizPanel(widget, event)
+ local appName = widget:GetUserData('appName')
+ AceConfigDialog.frame.closing[appName] = true
+ AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate)
+end
+
+--- Add an option table into the Blizzard Interface Options panel.
+-- You can optionally supply a descriptive name to use and a parent frame to use,
+-- as well as a path in the options table.\\
+-- If no name is specified, the appName will be used instead.
+--
+-- If you specify a proper `parent` (by name), the interface options will generate a
+-- tree layout. Note that only one level of children is supported, so the parent always
+-- has to be a head-level note.
+--
+-- This function returns a reference to the container frame registered with the Interface
+-- Options. You can use this reference to open the options with the API function
+-- `InterfaceOptionsFrame_OpenToCategory`.
+-- @param appName The application name as given to `:RegisterOptionsTable()`
+-- @param name A descriptive name to display in the options tree (defaults to appName)
+-- @param parent The parent to use in the interface options tree.
+-- @param ... The path in the options table to feed into the interface options panel.
+-- @return The reference to the frame registered into the Interface Options.
+function AceConfigDialog:AddToBlizOptions(appName, name, parent, ...)
+ local BlizOptions = AceConfigDialog.BlizOptions
+
+ local key = appName
+ for n = 1, select('#', ...) do
+ key = key..'\001'..select(n, ...)
+ end
+
+ if not BlizOptions[appName] then
+ BlizOptions[appName] = {}
+ end
+
+ if not BlizOptions[appName][key] then
+ local group = gui:Create("BlizOptionsGroup")
+ BlizOptions[appName][key] = group
+ group:SetName(name or appName, parent)
+
+ group:SetTitle(name or appName)
+ group:SetUserData('appName', appName)
+ if select('#', ...) > 0 then
+ local path = {}
+ for n = 1, select('#',...) do
+ tinsert(path, (select(n, ...)))
+ end
+ group:SetUserData('path', path)
+ end
+ 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 with the given path"):format(appName), 2)
+ end
+end
diff --git a/Libs/AceConfig-3.0/AceConfigDialog-3.0/AceConfigDialog-3.0.xml b/Libs/AceConfig-3.0/AceConfigDialog-3.0/AceConfigDialog-3.0.xml
new file mode 100644
index 0000000..69597e9
--- /dev/null
+++ b/Libs/AceConfig-3.0/AceConfigDialog-3.0/AceConfigDialog-3.0.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/Libs/AceConfig-3.0/AceConfigRegistry-3.0/AceConfigRegistry-3.0.lua b/Libs/AceConfig-3.0/AceConfigRegistry-3.0/AceConfigRegistry-3.0.lua
new file mode 100644
index 0000000..b6be483
--- /dev/null
+++ b/Libs/AceConfig-3.0/AceConfigRegistry-3.0/AceConfigRegistry-3.0.lua
@@ -0,0 +1,344 @@
+--- AceConfigRegistry-3.0 handles central registration of options tables in use by addons and modules.\\
+-- Options tables can be registered as raw tables, OR as function refs that return a table.\\
+-- Such functions receive three arguments: "uiType", "uiName", "appName". \\
+-- * 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.\\
+-- * The **appName** field is the options table name as given at registration time \\
+--
+-- :IterateOptionsTables() (and :GetOptionsTable() if only given one argument) return a function reference that the requesting config handling addon must call with valid "uiType", "uiName".
+-- @class file
+-- @name AceConfigRegistry-3.0
+-- @release $Id: AceConfigRegistry-3.0.lua 890 2009-12-06 12:50:05Z nevcairiel $
+local MAJOR, MINOR = "AceConfigRegistry-3.0", 11
+local AceConfigRegistry = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceConfigRegistry then return end
+
+AceConfigRegistry.tables = AceConfigRegistry.tables or {}
+
+local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0")
+
+if not AceConfigRegistry.callbacks then
+ AceConfigRegistry.callbacks = CallbackHandler:New(AceConfigRegistry)
+end
+
+-- Lua APIs
+local tinsert, tconcat = table.insert, table.concat
+local strfind, strmatch = string.find, string.match
+local type, tostring, select, pairs = type, tostring, select, pairs
+local error, assert = error, assert
+
+-----------------------------------------------------------------------
+-- Validating options table consistency:
+
+
+AceConfigRegistry.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(): "..tconcat(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,
+ descStyle=optstring,
+ 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,
+ fontSize=optstringfunc,
+ },
+ group={
+ args=istable,
+ plugins=opttable,
+ inline=optbool,
+ cmdInline=optbool,
+ guiInline=optbool,
+ dropdownInline=optbool,
+ dialogInline=optbool,
+ childGroups=optstring,
+ },
+ execute={
+ image=optstringfunc,
+ imageCoords=optmethodtable,
+ imageHeight=optnumber,
+ imageWidth=optnumber,
+ },
+ input={
+ pattern=optstring,
+ usage=optstring,
+ control=optstring,
+ dialogControl=optstring,
+ dropdownControl=optstring,
+ multiline=optboolnumber,
+ },
+ toggle={
+ tristate=optbool,
+ image=optstringfunc,
+ imageCoords=optmethodtable,
+ },
+ 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,
+ control=optstring,
+ dialogControl=optstring,
+ dropdownControl=optstring,
+ },
+ 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 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
+
+
+--- 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
+-- @param options The table to be validated
+-- @param name The name of the table to be validated (shown in any error message)
+-- @param errlvl (optional number) error level offset, default 0 (=errors point to the function calling :ValidateOptionsTable)
+function AceConfigRegistry: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
+
+--- Fires a "ConfigTableChange" callback for those listening in on it, allowing config GUIs to refresh.
+-- You should call this function if your options table changed from any outside event, like a game event
+-- or a timer.
+-- @param appName The application name as given to `:RegisterOptionsTable()`
+function AceConfigRegistry:NotifyChange(appName)
+ if not AceConfigRegistry.tables[appName] then return end
+ AceConfigRegistry.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
+
+--- Register an options table with the config registry.
+-- @param appName The application name as given to `:RegisterOptionsTable()`
+-- @param options The options table, OR a function reference that generates it on demand. \\
+-- See the top of the page for info on arguments passed to such functions.
+function AceConfigRegistry: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
+ AceConfigRegistry.tables[appName] = function(uiType, uiName, errlvl)
+ errlvl=(errlvl or 0)+1
+ validateGetterArgs(uiType, uiName, errlvl)
+ if not AceConfigRegistry.validated[uiType][appName] then
+ AceConfigRegistry:ValidateOptionsTable(options, appName, errlvl) -- upgradable
+ AceConfigRegistry.validated[uiType][appName] = true
+ end
+ return options
+ end
+ elseif type(options)=="function" then
+ AceConfigRegistry.tables[appName] = function(uiType, uiName, errlvl)
+ errlvl=(errlvl or 0)+1
+ validateGetterArgs(uiType, uiName, errlvl)
+ local tab = assert(options(uiType, uiName, appName))
+ if not AceConfigRegistry.validated[uiType][appName] then
+ AceConfigRegistry:ValidateOptionsTable(tab, appName, errlvl) -- upgradable
+ AceConfigRegistry.validated[uiType][appName] = true
+ end
+ return tab
+ end
+ else
+ error(MAJOR..": RegisterOptionsTable(appName, options): 'options' - expected table or function reference", 2)
+ end
+end
+
+--- Returns an iterator of ["appName"]=funcref pairs
+function AceConfigRegistry:IterateOptionsTables()
+ return pairs(AceConfigRegistry.tables)
+end
+
+
+
+
+--- Query the registry for a specific options table.
+-- 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.
+-- @param appName The application name as given to `:RegisterOptionsTable()`
+-- @param uiType The type of UI to get the table for, one of "cmd", "dropdown", "dialog"
+-- @param uiName The name of the library/addon querying for the table, e.g. "MyLib-1.0"
+function AceConfigRegistry:GetOptionsTable(appName, uiType, uiName)
+ local f = AceConfigRegistry.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
diff --git a/Libs/AceConfig-3.0/AceConfigRegistry-3.0/AceConfigRegistry-3.0.xml b/Libs/AceConfig-3.0/AceConfigRegistry-3.0/AceConfigRegistry-3.0.xml
new file mode 100644
index 0000000..f65f5dd
--- /dev/null
+++ b/Libs/AceConfig-3.0/AceConfigRegistry-3.0/AceConfigRegistry-3.0.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/Libs/AceConsole-3.0/AceConsole-3.0.lua b/Libs/AceConsole-3.0/AceConsole-3.0.lua
new file mode 100644
index 0000000..07b046f
--- /dev/null
+++ b/Libs/AceConsole-3.0/AceConsole-3.0.lua
@@ -0,0 +1,250 @@
+--- **AceConsole-3.0** provides registration facilities for slash commands.
+-- You can register slash commands to your custom functions and use the `GetArgs` function to parse them
+-- to your addons individual needs.
+--
+-- **AceConsole-3.0** can be embeded into your addon, either explicitly by calling AceConsole:Embed(MyAddon) or by
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceConsole itself.\\
+-- It is recommended to embed AceConsole, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceConsole.
+-- @class file
+-- @name AceConsole-3.0
+-- @release $Id: AceConsole-3.0.lua 878 2009-11-02 18:51:58Z nevcairiel $
+local MAJOR,MINOR = "AceConsole-3.0", 7
+
+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
+
+-- Lua APIs
+local tconcat, tostring, select = table.concat, tostring, select
+local type, pairs, error = type, pairs, error
+local format, strfind, strsub = string.format, string.find, string.sub
+local max = math.max
+
+-- WoW APIs
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: DEFAULT_CHAT_FRAME, SlashCmdList, hash_SlashCmdList
+
+local tmp={}
+local function Print(self,frame,...)
+ local n=0
+ if self ~= AceConsole then
+ n=n+1
+ tmp[n] = "|cff33ff99"..tostring( self ).."|r:"
+ end
+ for i=1, select("#", ...) do
+ n=n+1
+ tmp[n] = tostring(select(i, ...))
+ end
+ frame:AddMessage( tconcat(tmp," ",1,n) )
+end
+
+--- Print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
+-- @paramsig [chatframe ,] ...
+-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
+-- @param ... List of any values to be printed
+function AceConsole:Print(...)
+ local frame = ...
+ if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member?
+ return Print(self, frame, select(2,...))
+ else
+ return Print(self, DEFAULT_CHAT_FRAME, ...)
+ end
+end
+
+
+--- Formatted (using format()) print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
+-- @paramsig [chatframe ,] "format"[, ...]
+-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
+-- @param format Format string - same syntax as standard Lua format()
+-- @param ... Arguments to the format string
+function AceConsole:Printf(...)
+ local frame = ...
+ if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member?
+ return Print(self, frame, format(select(2,...)))
+ else
+ return Print(self, DEFAULT_CHAT_FRAME, format(...))
+ end
+end
+
+
+
+
+--- Register a simple chat command
+-- @param command Chat command to be registered WITHOUT leading "/"
+-- @param func Function to call when the slash command is being used (funcref or methodname)
+-- @param persist if false, the command will be soft disabled/enabled when aceconsole is used as a mixin (default: true)
+function AceConsole:RegisterChatCommand( command, func, persist )
+ if type(command)~="string" then error([[Usage: AceConsole:RegisterChatCommand( "command", func[, persist ]): '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 type( func ) == "string" then
+ SlashCmdList[name] = function(input, editBox)
+ self[func](self, input, editBox)
+ 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
+
+--- Unregister a chatcommand
+-- @param command Chat command to be unregistered WITHOUT leading "/"
+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
+
+--- Get an iterator over all Chat Commands registered with AceConsole
+-- @return Iterator (pairs) over all commands
+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
+
+
+--- Retreive one or more space-separated arguments from a string.
+-- Treats quoted strings and itemlinks as non-spaced.
+-- @param string The raw argument string
+-- @param numargs How many arguments to get (default 1)
+-- @param startpos Where in the string to start scanning (default 1)
+-- @return 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",
+ "Printf",
+ "RegisterChatCommand",
+ "UnregisterChatCommand",
+ "GetArgs",
+}
+
+-- Embeds AceConsole into the target object making the functions from the mixins list available on target:..
+-- @param target target object to embed AceBucket in
+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
diff --git a/Libs/AceConsole-3.0/AceConsole-3.0.xml b/Libs/AceConsole-3.0/AceConsole-3.0.xml
new file mode 100644
index 0000000..e3e8b6f
--- /dev/null
+++ b/Libs/AceConsole-3.0/AceConsole-3.0.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/Libs/AceDB-3.0/AceDB-3.0.lua b/Libs/AceDB-3.0/AceDB-3.0.lua
new file mode 100644
index 0000000..6f51265
--- /dev/null
+++ b/Libs/AceDB-3.0/AceDB-3.0.lua
@@ -0,0 +1,712 @@
+--- **AceDB-3.0** manages the SavedVariables of your addon.
+-- It offers profile management, smart defaults and namespaces for modules.\\
+-- Data can be saved in different data-types, depending on its intended usage.
+-- The most common data-type is the `profile` type, which allows the user to choose
+-- the active profile, and manage the profiles of all of his characters.\\
+-- The following data types are available:
+-- * **char** Character-specific data. Every character has its own database.
+-- * **realm** Realm-specific data. All of the players characters on the same realm share this database.
+-- * **class** Class-specific data. All of the players characters of the same class share this database.
+-- * **race** Race-specific data. All of the players characters of the same race share this database.
+-- * **faction** Faction-specific data. All of the players characters of the same faction share this database.
+-- * **factionrealm** Faction and realm specific data. All of the players characters on the same realm and of the same faction share this database.
+-- * **global** Global Data. All characters on the same account share this database.
+-- * **profile** Profile-specific data. All characters using the same profile share this database. The user can control which profile should be used.
+--
+-- Creating a new Database using the `:New` function will return a new DBObject. A database will inherit all functions
+-- of the DBObjectLib listed here. \\
+-- If you create a new namespaced child-database (`:RegisterNamespace`), you'll get a DBObject as well, but note
+-- that the child-databases cannot individually change their profile, and are linked to their parents profile - and because of that,
+-- the profile related APIs are not available. Only `:RegisterDefaults` and `:ResetProfile` are available on child-databases.
+--
+-- For more details on how to use AceDB-3.0, see the [[AceDB-3.0 Tutorial]].
+--
+-- You may also be interested in [[libdualspec-1-0|LibDualSpec-1.0]] to do profile switching automatically when switching specs.
+--
+-- @usage
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("DBExample")
+--
+-- -- declare defaults to be used in the DB
+-- local defaults = {
+-- profile = {
+-- setting = true,
+-- }
+-- }
+--
+-- function MyAddon:OnInitialize()
+-- -- Assuming the .toc says ## SavedVariables: MyAddonDB
+-- self.db = LibStub("AceDB-3.0"):New("MyAddonDB", defaults, true)
+-- end
+-- @class file
+-- @name AceDB-3.0.lua
+-- @release $Id: AceDB-3.0.lua 877 2009-11-02 15:56:50Z nevcairiel $
+local ACEDB_MAJOR, ACEDB_MINOR = "AceDB-3.0", 19
+local AceDB, oldminor = LibStub:NewLibrary(ACEDB_MAJOR, ACEDB_MINOR)
+
+if not AceDB then return end -- No upgrade needed
+
+-- Lua APIs
+local type, pairs, next, error = type, pairs, next, error
+local setmetatable, getmetatable, rawset, rawget = setmetatable, getmetatable, rawset, rawget
+
+-- WoW APIs
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: LibStub
+
+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)
+ -- remove all metatables from the db, so we don't accidentally create new sub-tables through them
+ setmetatable(db, nil)
+ -- loop through the defaults and remove their content
+ 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 and (not blocker or blocker[key] == nil) then
+ removeDefaults(value, v)
+ -- if the table is empty afterwards, remove it
+ if next(value) == nil 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 next(db[k]) == nil 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
+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
+
+ -- map "true" to our "Default" profile
+ if defaultProfile == true then defaultProfile = "Default" end
+
+ local profileKey
+ if not parent then
+ -- Make a container for profile keys
+ if not sv.profileKeys then sv.profileKeys = {} end
+
+ -- Try to get the profile selected from the char db
+ profileKey = sv.profileKeys[charKey] or defaultProfile or charKey
+
+ -- save the selected profile for later
+ sv.profileKeys[charKey] = profileKey
+ else
+ -- Use the profile of the parents DB
+ profileKey = parent.keys.profile or defaultProfile or charKey
+
+ -- clear the profileKeys in the DB, namespaces don't need to store them
+ sv.profileKeys = nil
+ end
+
+ -- 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
+---------------------------------------------------------------------------]]
+
+--- Sets the defaults table for the given database object by clearing any
+-- that are currently set, and then setting the new defaults.
+-- @param defaults A table of defaults for this database
+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
+
+--- Changes the profile of the database and all of it's namespaces to the
+-- supplied named profile
+-- @param name The name of the profile to set as the current 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
+
+ -- Callback: OnProfileShutdown, database
+ self.callbacks:Fire("OnProfileShutdown", self)
+
+ if oldProfile and defaults then
+ -- Remove the defaults from the old profile
+ removeDefaults(oldProfile, defaults)
+ end
+
+ self.profile = nil
+ self.keys["profile"] = name
+
+ -- if the storage exists, save the new profile
+ -- this won't exist on namespaces.
+ if self.sv.profileKeys then
+ self.sv.profileKeys[charKey] = name
+ end
+
+ -- 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
+
+--- 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.
+-- @param tbl A table to store the profile names in (optional)
+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
+
+--- Returns the current profile name used by the database
+function DBObjectLib:GetCurrentProfile()
+ return self.keys.profile
+end
+
+--- Deletes a named profile. This profile must not be the active profile.
+-- @param name The name of the profile to be deleted
+-- @param silent If true, do not raise an error when the profile does not exist
+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
+
+--- Copies a named profile into the current profile, overwriting any conflicting
+-- settings.
+-- @param name The name of the profile to be copied into the current profile
+-- @param silent If true, do not raise an error when the profile does not exist
+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, nil, true)
+
+ 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
+
+--- Resets the current profile to the default values (if specified).
+-- @param noChildren if set to true, the reset will not be populated to the child namespaces of this DB object
+-- @param noCallbacks if set to true, won't fire the OnProfileReset callback
+function DBObjectLib:ResetProfile(noChildren, noCallbacks)
+ 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, nil, noCallbacks)
+ end
+ end
+
+ -- Callback: OnProfileReset, database
+ if not noCallbacks then
+ self.callbacks:Fire("OnProfileReset", self)
+ end
+end
+
+--- Resets the entire database, using the string defaultProfile as the new default
+-- profile.
+-- @param defaultProfile The profile name to use as the default
+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
+
+--- 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
+-- @param name The name of the new namespace
+-- @param defaults A table of values to use as defaults
+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
+
+--- Returns an already existing namespace from the database object.
+-- @param name The name of the new namespace
+-- @param silent if true, the addon is optional, silently return nil if its not found
+-- @usage
+-- local namespace = self.db:GetNamespace('namespace')
+-- @return the namespace object if found
+function DBObjectLib:GetNamespace(name, silent)
+ if type(name) ~= "string" then
+ error("Usage: AceDBObject:GetNamespace(name): 'name' - string expected.", 2)
+ end
+ if not silent and not (self.children and self.children[name]) then
+ error ("Usage: AceDBObject:GetNamespace(name): 'name' - namespace does not exist.", 2)
+ end
+ if not self.children then self.children = {} end
+ return self.children[name]
+end
+
+--[[-------------------------------------------------------------------------
+ AceDB Exposed Methods
+---------------------------------------------------------------------------]]
+
+--- Creates a new database object that can be used to handle database settings and profiles.
+-- By default, an empty DB is created, using a character specific profile.
+--
+-- You can override the default profile used by passing any profile name as the third argument,
+-- or by passing //true// as the third argument to use a globally shared profile called "Default".
+--
+-- Note that there is no token replacement in the default profile name, passing a defaultProfile as "char"
+-- will use a profile named "char", and not a character-specific profile.
+-- @param tbl The name of variable, or table to use for the database
+-- @param defaults A table of database defaults
+-- @param defaultProfile The name of the default profile. If not set, a character specific profile will be used as the default.
+-- You can also pass //true// to use a shared global profile called "Default".
+-- @usage
+-- -- Create an empty DB using a character-specific default profile.
+-- self.db = LibStub("AceDB-3.0"):New("MyAddonDB")
+-- @usage
+-- -- Create a DB using defaults and using a shared default profile
+-- self.db = LibStub("AceDB-3.0"):New("MyAddonDB", defaults, true)
+function AceDB:New(tbl, defaults, defaultProfile)
+ if type(tbl) == "string" then
+ local name = tbl
+ tbl = _G[name]
+ if not tbl then
+ tbl = {}
+ _G[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" and defaultProfile ~= true then
+ error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaultProfile' - string or true 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
+ db.ResetProfile = DBObjectLib.ResetProfile
+ end
+end
diff --git a/Libs/AceDB-3.0/AceDB-3.0.xml b/Libs/AceDB-3.0/AceDB-3.0.xml
new file mode 100644
index 0000000..e825f7b
--- /dev/null
+++ b/Libs/AceDB-3.0/AceDB-3.0.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/Libs/AceDBOptions-3.0/AceDBOptions-3.0.lua b/Libs/AceDBOptions-3.0/AceDBOptions-3.0.lua
new file mode 100644
index 0000000..d40172d
--- /dev/null
+++ b/Libs/AceDBOptions-3.0/AceDBOptions-3.0.lua
@@ -0,0 +1,420 @@
+--- AceDBOptions-3.0 provides a universal AceConfig options screen for managing AceDB-3.0 profiles.
+-- @class file
+-- @name AceDBOptions-3.0
+-- @release $Id: AceDBOptions-3.0.lua 895 2009-12-06 16:28:55Z nevcairiel $
+local ACEDBO_MAJOR, ACEDBO_MINOR = "AceDBOptions-3.0", 11
+local AceDBOptions, oldminor = LibStub:NewLibrary(ACEDBO_MAJOR, ACEDBO_MINOR)
+
+if not AceDBOptions then return end -- No upgrade needed
+
+-- Lua APIs
+local pairs, next = pairs, next
+
+-- WoW APIs
+local UnitClass = UnitClass
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: NORMAL_FONT_COLOR_CODE, FONT_COLOR_CODE_CLOSE
+
+AceDBOptions.optionTables = AceDBOptions.optionTables or {}
+AceDBOptions.handlers = AceDBOptions.handlers or {}
+
+--[[
+ Localization of AceDBOptions-3.0
+]]
+
+local L = {
+ default = "Default",
+ intro = "You can change the active database profile, so you can have different settings for every character.",
+ reset_desc = "Reset the current profile back to its default values, in case your configuration is broken, or you simply want to start over.",
+ reset = "Reset Profile",
+ reset_sub = "Reset the current profile to the default",
+ choose_desc = "You can either create a new profile by entering a name in the editbox, or choose one of the already exisiting profiles.",
+ new = "New",
+ new_sub = "Create a new empty profile.",
+ choose = "Existing Profiles",
+ choose_sub = "Select one of your currently available profiles.",
+ copy_desc = "Copy the settings from one existing profile into the currently active profile.",
+ copy = "Copy From",
+ delete_desc = "Delete existing and unused profiles from the database to save space, and cleanup the SavedVariables file.",
+ delete = "Delete a Profile",
+ delete_sub = "Deletes a profile from the database.",
+ delete_confirm = "Are you sure you want to delete the selected profile?",
+ profiles = "Profiles",
+ profiles_sub = "Manage Profiles",
+ current = "Current Profile:",
+}
+
+local LOCALE = GetLocale()
+if LOCALE == "deDE" then
+ L["default"] = "Standard"
+ L["intro"] = "Hier kannst du das aktive Datenbankprofile \195\164ndern, damit du verschiedene Einstellungen f\195\188r jeden Charakter erstellen kannst, wodurch eine sehr flexible Konfiguration m\195\182glich wird."
+ L["reset_desc"] = "Setzt das momentane Profil auf Standardwerte zur\195\188ck, f\195\188r den Fall das mit der Konfiguration etwas schief lief oder weil du einfach neu starten willst."
+ L["reset"] = "Profil zur\195\188cksetzen"
+ L["reset_sub"] = "Das aktuelle Profil auf Standard zur\195\188cksetzen."
+ L["choose_desc"] = "Du kannst ein neues Profil erstellen, indem du einen neuen Namen in der Eingabebox 'Neu' eingibst, oder w\195\164hle eines der vorhandenen Profile aus."
+ L["new"] = "Neu"
+ L["new_sub"] = "Ein neues Profil erstellen."
+ L["choose"] = "Vorhandene Profile"
+ L["choose_sub"] = "W\195\164hlt ein bereits vorhandenes Profil aus."
+ L["copy_desc"] = "Kopiere die Einstellungen von einem vorhandenen Profil in das aktive Profil."
+ L["copy"] = "Kopieren von..."
+ L["delete_desc"] = "L\195\182sche vorhandene oder unbenutzte Profile aus der Datenbank um Platz zu sparen und um die SavedVariables Datei 'sauber' zu halten."
+ L["delete"] = "Profil l\195\182schen"
+ L["delete_sub"] = "L\195\182scht ein Profil aus der Datenbank."
+ L["delete_confirm"] = "Willst du das ausgew\195\164hlte Profil wirklich l\195\182schen?"
+ L["profiles"] = "Profile"
+ L["profiles_sub"] = "Profile verwalten"
+ --L["current"] = "Current Profile:"
+elseif LOCALE == "frFR" then
+ L["default"] = "D\195\169faut"
+ L["intro"] = "Vous pouvez changer le profil actuel afin d'avoir des param\195\168tres diff\195\169rents pour chaque personnage, permettant ainsi d'avoir une configuration tr\195\168s flexible."
+ L["reset_desc"] = "R\195\169initialise le profil actuel au cas o\195\185 votre configuration est corrompue ou si vous voulez tout simplement faire table rase."
+ L["reset"] = "R\195\169initialiser le profil"
+ L["reset_sub"] = "R\195\169initialise le profil actuel avec les param\195\168tres par d\195\169faut."
+ L["choose_desc"] = "Vous pouvez cr\195\169er un nouveau profil en entrant un nouveau nom dans la bo\195\174te de saisie, ou en choississant un des profils d\195\169j\195\160 existants."
+ L["new"] = "Nouveau"
+ L["new_sub"] = "Cr\195\169\195\169e un nouveau profil vierge."
+ L["choose"] = "Profils existants"
+ L["choose_sub"] = "Permet de choisir un des profils d\195\169j\195\160 disponibles."
+ L["copy_desc"] = "Copie les param\195\168tres d'un profil d\195\169j\195\160 existant dans le profil actuellement actif."
+ L["copy"] = "Copier \195\160 partir de"
+ L["delete_desc"] = "Supprime les profils existants inutilis\195\169s de la base de donn\195\169es afin de gagner de la place et de nettoyer le fichier SavedVariables."
+ L["delete"] = "Supprimer un profil"
+ L["delete_sub"] = "Supprime un profil de la base de donn\195\169es."
+ L["delete_confirm"] = "Etes-vous s\195\187r de vouloir supprimer le profil s\195\169lectionn\195\169 ?"
+ L["profiles"] = "Profils"
+ L["profiles_sub"] = "Gestion des profils"
+ --L["current"] = "Current Profile:"
+elseif LOCALE == "koKR" then
+ L["default"] = "기본값"
+ L["intro"] = "모든 캐릭터의 다양한 설정과 사용중인 데이터베이스 프로필, 어느것이던지 매우 다루기 쉽게 바꿀수 있습니다."
+ L["reset_desc"] = "단순히 다시 새롭게 구성을 원하는 경우, 현재 프로필을 기본값으로 초기화 합니다."
+ L["reset"] = "프로필 초기화"
+ L["reset_sub"] = "현재의 프로필을 기본값으로 초기화 합니다"
+ L["choose_desc"] = "새로운 이름을 입력하거나, 이미 있는 프로필중 하나를 선택하여 새로운 프로필을 만들 수 있습니다."
+ L["new"] = "새로운 프로필"
+ L["new_sub"] = "새로운 프로필을 만듭니다."
+ L["choose"] = "프로필 선택"
+ L["choose_sub"] = "당신이 현재 이용할수 있는 프로필을 선택합니다."
+ L["copy_desc"] = "현재 사용중인 프로필에, 선택한 프로필의 설정을 복사합니다."
+ L["copy"] = "복사"
+ L["delete_desc"] = "데이터베이스에 사용중이거나 저장된 프로파일 삭제로 SavedVariables 파일의 정리와 공간 절약이 됩니다."
+ L["delete"] = "프로필 삭제"
+ L["delete_sub"] = "데이터베이스의 프로필을 삭제합니다."
+ L["delete_confirm"] = "정말로 선택한 프로필의 삭제를 원하십니까?"
+ L["profiles"] = "프로필"
+ L["profiles_sub"] = "프로필 설정"
+ --L["current"] = "Current Profile:"
+elseif LOCALE == "esES" or LOCALE == "esMX" then
+ L["default"] = "Por defecto"
+ L["intro"] = "Puedes cambiar el perfil activo de tal manera que cada personaje tenga diferentes configuraciones."
+ L["reset_desc"] = "Reinicia el perfil actual a los valores por defectos, en caso de que se haya estropeado la configuración o quieras volver a empezar de nuevo."
+ L["reset"] = "Reiniciar Perfil"
+ L["reset_sub"] = "Reinicar el perfil actual al de por defecto"
+ L["choose_desc"] = "Puedes crear un nuevo perfil introduciendo un nombre en el recuadro o puedes seleccionar un perfil de los ya existentes."
+ L["new"] = "Nuevo"
+ L["new_sub"] = "Crear un nuevo perfil vacio."
+ L["choose"] = "Perfiles existentes"
+ L["choose_sub"] = "Selecciona uno de los perfiles disponibles."
+ L["copy_desc"] = "Copia los ajustes de un perfil existente al perfil actual."
+ L["copy"] = "Copiar de"
+ L["delete_desc"] = "Borra los perfiles existentes y sin uso de la base de datos para ganar espacio y limpiar el archivo SavedVariables."
+ L["delete"] = "Borrar un Perfil"
+ L["delete_sub"] = "Borra un perfil de la base de datos."
+ L["delete_confirm"] = "¿Estas seguro que quieres borrar el perfil seleccionado?"
+ L["profiles"] = "Perfiles"
+ L["profiles_sub"] = "Manejar Perfiles"
+ --L["current"] = "Current Profile:"
+elseif LOCALE == "zhTW" then
+ L["default"] = "預設"
+ L["intro"] = "你可以選擇一個活動的資料設定檔,這樣你的每個角色就可以擁有不同的設定值,可以給你的插件設定帶來極大的靈活性。"
+ L["reset_desc"] = "將當前的設定檔恢復到它的預設值,用於你的設定檔損壞,或者你只是想重來的情況。"
+ L["reset"] = "重置設定檔"
+ L["reset_sub"] = "將當前的設定檔恢復為預設值"
+ L["choose_desc"] = "你可以通過在文本框內輸入一個名字創立一個新的設定檔,也可以選擇一個已經存在的設定檔。"
+ L["new"] = "新建"
+ L["new_sub"] = "新建一個空的設定檔。"
+ L["choose"] = "現有的設定檔"
+ L["choose_sub"] = "從當前可用的設定檔裏面選擇一個。"
+ L["copy_desc"] = "從當前某個已保存的設定檔複製到當前正使用的設定檔。"
+ L["copy"] = "複製自"
+ L["delete_desc"] = "從資料庫裏刪除不再使用的設定檔,以節省空間,並且清理SavedVariables檔。"
+ L["delete"] = "刪除一個設定檔"
+ L["delete_sub"] = "從資料庫裏刪除一個設定檔。"
+ L["delete_confirm"] = "你確定要刪除所選擇的設定檔嗎?"
+ L["profiles"] = "設定檔"
+ L["profiles_sub"] = "管理設定檔"
+ --L["current"] = "Current Profile:"
+elseif LOCALE == "zhCN" then
+ L["default"] = "默认"
+ L["intro"] = "你可以选择一个活动的数据配置文件,这样你的每个角色就可以拥有不同的设置值,可以给你的插件配置带来极大的灵活性。"
+ L["reset_desc"] = "将当前的配置文件恢复到它的默认值,用于你的配置文件损坏,或者你只是想重来的情况。"
+ L["reset"] = "重置配置文件"
+ L["reset_sub"] = "将当前的配置文件恢复为默认值"
+ L["choose_desc"] = "你可以通过在文本框内输入一个名字创立一个新的配置文件,也可以选择一个已经存在的配置文件。"
+ L["new"] = "新建"
+ L["new_sub"] = "新建一个空的配置文件。"
+ L["choose"] = "现有的配置文件"
+ L["choose_sub"] = "从当前可用的配置文件里面选择一个。"
+ L["copy_desc"] = "从当前某个已保存的配置文件复制到当前正使用的配置文件。"
+ L["copy"] = "复制自"
+ L["delete_desc"] = "从数据库里删除不再使用的配置文件,以节省空间,并且清理SavedVariables文件。"
+ L["delete"] = "删除一个配置文件"
+ L["delete_sub"] = "从数据库里删除一个配置文件。"
+ L["delete_confirm"] = "你确定要删除所选择的配置文件么?"
+ L["profiles"] = "配置文件"
+ L["profiles_sub"] = "管理配置文件"
+ --L["current"] = "Current Profile:"
+elseif LOCALE == "ruRU" then
+ L["default"] = "По умолчанию"
+ L["intro"] = "Изменяя активный профиль, вы можете задать различные настройки модификаций для каждого персонажа."
+ L["reset_desc"] = "Если ваша конфигурации испорчена или если вы хотите настроить всё заново - сбросьте текущий профиль на стандартные значения."
+ L["reset"] = "Сброс профиля"
+ L["reset_sub"] = "Сброс текущего профиля на стандартный"
+ L["choose_desc"] = "Вы можете создать новый профиль, введя название в поле ввода, или выбрать один из уже существующих профилей."
+ L["new"] = "Новый"
+ L["new_sub"] = "Создать новый чистый профиль"
+ L["choose"] = "Существующие профили"
+ L["choose_sub"] = "Выбор одиного из уже доступных профилей"
+ L["copy_desc"] = "Скопировать настройки из выбранного профиля в активный."
+ L["copy"] = "Скопировать из"
+ L["delete_desc"] = "Удалить существующий и неиспользуемый профиль из БД для сохранения места, и очистить SavedVariables файл."
+ L["delete"] = "Удалить профиль"
+ L["delete_sub"] = "Удаление профиля из БД"
+ L["delete_confirm"] = "Вы уверены, что вы хотите удалить выбранный профиль?"
+ L["profiles"] = "Профили"
+ L["profiles_sub"] = "Управление профилями"
+ --L["current"] = "Current Profile:"
+end
+
+local defaultProfiles
+local tmpprofiles = {}
+
+-- Get a list of available profiles for the specified database.
+-- You can specify which profiles to include/exclude in the list using the two boolean parameters listed below.
+-- @param db The db object to retrieve the profiles from
+-- @param common If true, getProfileList will add the default profiles to the return list, even if they have not been created yet
+-- @param nocurrent If true, then getProfileList will not display the current profile in the list
+-- @return Hashtable of all profiles with the internal name as keys and the display name as value.
+local function getProfileList(db, common, nocurrent)
+ local profiles = {}
+
+ -- copy existing profiles into the table
+ local currentProfile = db:GetCurrentProfile()
+ for i,v in pairs(db:GetProfiles(tmpprofiles)) do
+ if not (nocurrent and v == currentProfile) then
+ profiles[v] = v
+ end
+ end
+
+ -- add our default profiles to choose from ( or rename existing profiles)
+ for k,v in pairs(defaultProfiles) do
+ if (common or profiles[k]) and not (nocurrent and k == currentProfile) then
+ profiles[k] = v
+ end
+ end
+
+ return profiles
+end
+
+--[[
+ OptionsHandlerPrototype
+ prototype class for handling the options in a sane way
+]]
+local OptionsHandlerPrototype = {}
+
+--[[ Reset the profile ]]
+function OptionsHandlerPrototype:Reset()
+ self.db:ResetProfile()
+end
+
+--[[ Set the profile to value ]]
+function OptionsHandlerPrototype:SetProfile(info, value)
+ self.db:SetProfile(value)
+end
+
+--[[ returns the currently active profile ]]
+function OptionsHandlerPrototype:GetCurrentProfile()
+ return self.db:GetCurrentProfile()
+end
+
+--[[
+ List all active profiles
+ you can control the output with the .arg variable
+ currently four modes are supported
+
+ (empty) - return all available profiles
+ "nocurrent" - returns all available profiles except the currently active profile
+ "common" - returns all avaialble profiles + some commonly used profiles ("char - realm", "realm", "class", "Default")
+ "both" - common except the active profile
+]]
+function OptionsHandlerPrototype:ListProfiles(info)
+ local arg = info.arg
+ local profiles
+ if arg == "common" and not self.noDefaultProfiles then
+ profiles = getProfileList(self.db, true, nil)
+ elseif arg == "nocurrent" then
+ profiles = getProfileList(self.db, nil, true)
+ elseif arg == "both" then -- currently not used
+ profiles = getProfileList(self.db, (not self.noDefaultProfiles) and true, true)
+ else
+ profiles = getProfileList(self.db)
+ end
+
+ return profiles
+end
+
+function OptionsHandlerPrototype:HasNoProfiles(info)
+ local profiles = self:ListProfiles(info)
+ return ((not next(profiles)) and true or false)
+end
+
+--[[ Copy a profile ]]
+function OptionsHandlerPrototype:CopyProfile(info, value)
+ self.db:CopyProfile(value)
+end
+
+--[[ Delete a profile from the db ]]
+function OptionsHandlerPrototype:DeleteProfile(info, value)
+ self.db:DeleteProfile(value)
+end
+
+--[[ fill defaultProfiles with some generic values ]]
+local function generateDefaultProfiles(db)
+ defaultProfiles = {
+ ["Default"] = L["default"],
+ [db.keys.char] = db.keys.char,
+ [db.keys.realm] = db.keys.realm,
+ [db.keys.class] = UnitClass("player")
+ }
+end
+
+--[[ create and return a handler object for the db, or upgrade it if it already existed ]]
+local function getOptionsHandler(db, noDefaultProfiles)
+ if not defaultProfiles then
+ generateDefaultProfiles(db)
+ end
+
+ local handler = AceDBOptions.handlers[db] or { db = db, noDefaultProfiles = noDefaultProfiles }
+
+ for k,v in pairs(OptionsHandlerPrototype) do
+ handler[k] = v
+ end
+
+ AceDBOptions.handlers[db] = handler
+ return handler
+end
+
+--[[
+ the real options table
+]]
+local optionsTable = {
+ desc = {
+ order = 1,
+ type = "description",
+ name = L["intro"] .. "\n",
+ },
+ descreset = {
+ order = 9,
+ type = "description",
+ name = L["reset_desc"],
+ },
+ reset = {
+ order = 10,
+ type = "execute",
+ name = L["reset"],
+ desc = L["reset_sub"],
+ func = "Reset",
+ },
+ current = {
+ order = 11,
+ type = "description",
+ name = function(info) return L["current"] .. " " .. NORMAL_FONT_COLOR_CODE .. info.handler:GetCurrentProfile() .. FONT_COLOR_CODE_CLOSE end,
+ width = "default",
+ },
+ choosedesc = {
+ order = 20,
+ type = "description",
+ name = "\n" .. L["choose_desc"],
+ },
+ new = {
+ name = L["new"],
+ desc = L["new_sub"],
+ type = "input",
+ order = 30,
+ get = false,
+ set = "SetProfile",
+ },
+ choose = {
+ name = L["choose"],
+ desc = L["choose_sub"],
+ type = "select",
+ order = 40,
+ get = "GetCurrentProfile",
+ set = "SetProfile",
+ values = "ListProfiles",
+ arg = "common",
+ },
+ copydesc = {
+ order = 50,
+ type = "description",
+ name = "\n" .. L["copy_desc"],
+ },
+ copyfrom = {
+ order = 60,
+ type = "select",
+ name = L["copy"],
+ desc = L["copy_desc"],
+ get = false,
+ set = "CopyProfile",
+ values = "ListProfiles",
+ disabled = "HasNoProfiles",
+ arg = "nocurrent",
+ },
+ deldesc = {
+ order = 70,
+ type = "description",
+ name = "\n" .. L["delete_desc"],
+ },
+ delete = {
+ order = 80,
+ type = "select",
+ name = L["delete"],
+ desc = L["delete_sub"],
+ get = false,
+ set = "DeleteProfile",
+ values = "ListProfiles",
+ disabled = "HasNoProfiles",
+ arg = "nocurrent",
+ confirm = true,
+ confirmText = L["delete_confirm"],
+ },
+}
+
+--- Get/Create a option table that you can use in your addon to control the profiles of AceDB-3.0.
+-- @param db The database object to create the options table for.
+-- @return The options table to be used in AceConfig-3.0
+-- @usage
+-- -- Assuming `options` is your top-level options table and `self.db` is your database:
+-- options.args.profiles = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db)
+function AceDBOptions:GetOptionsTable(db, noDefaultProfiles)
+ local tbl = AceDBOptions.optionTables[db] or {
+ type = "group",
+ name = L["profiles"],
+ desc = L["profiles_sub"],
+ }
+
+ tbl.handler = getOptionsHandler(db, noDefaultProfiles)
+ tbl.args = optionsTable
+
+ AceDBOptions.optionTables[db] = tbl
+ return tbl
+end
+
+-- upgrade existing tables
+for db,tbl in pairs(AceDBOptions.optionTables) do
+ tbl.handler = getOptionsHandler(db)
+ tbl.args = optionsTable
+end
diff --git a/Libs/AceDBOptions-3.0/AceDBOptions-3.0.xml b/Libs/AceDBOptions-3.0/AceDBOptions-3.0.xml
new file mode 100644
index 0000000..a34c086
--- /dev/null
+++ b/Libs/AceDBOptions-3.0/AceDBOptions-3.0.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/Libs/AceEvent-3.0/AceEvent-3.0.lua b/Libs/AceEvent-3.0/AceEvent-3.0.lua
new file mode 100644
index 0000000..dd86a7c
--- /dev/null
+++ b/Libs/AceEvent-3.0/AceEvent-3.0.lua
@@ -0,0 +1,126 @@
+--- AceEvent-3.0 provides event registration and secure dispatching.
+-- All dispatching is done using **CallbackHandler-1.0**. AceEvent is a simple wrapper around
+-- CallbackHandler, and dispatches all game events or addon message to the registrees.
+--
+-- **AceEvent-3.0** can be embeded into your addon, either explicitly by calling AceEvent:Embed(MyAddon) or by
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceEvent itself.\\
+-- It is recommended to embed AceEvent, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceEvent.
+-- @class file
+-- @name AceEvent-3.0
+-- @release $Id: AceEvent-3.0.lua 877 2009-11-02 15:56:50Z nevcairiel $
+local MAJOR, MINOR = "AceEvent-3.0", 3
+local AceEvent = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceEvent then return end
+
+-- Lua APIs
+local pairs = pairs
+
+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",
+}
+
+--- Register for a Blizzard Event.
+-- The callback will always be called with the event as the first argument, and if supplied, the `arg` as second argument.
+-- Any arguments to the event will be passed on after that.
+-- @name AceEvent:RegisterEvent
+-- @class function
+-- @paramsig event[, callback [, arg]]
+-- @param event The event to register for
+-- @param callback The callback function to call when the event is triggered (funcref or method, defaults to a method with the event name)
+-- @param arg An optional argument to pass to the callback function
+
+--- Unregister an event.
+-- @name AceEvent:UnregisterEvent
+-- @class function
+-- @paramsig event
+-- @param event The event to unregister
+
+--- Register for a custom AceEvent-internal message.
+-- The callback will always be called with the event as the first argument, and if supplied, the `arg` as second argument.
+-- Any arguments to the event will be passed on after that.
+-- @name AceEvent:RegisterMessage
+-- @class function
+-- @paramsig message[, callback [, arg]]
+-- @param message The message to register for
+-- @param callback The callback function to call when the message is triggered (funcref or method, defaults to a method with the event name)
+-- @param arg An optional argument to pass to the callback function
+
+--- Unregister a message
+-- @name AceEvent:UnregisterMessage
+-- @class function
+-- @paramsig message
+-- @param message The message to unregister
+
+--- Send a message over the AceEvent-3.0 internal message system to other addons registered for this message.
+-- @name AceEvent:SendMessage
+-- @class function
+-- @paramsig message, ...
+-- @param message The message to send
+-- @param ... Any arguments to the message
+
+
+-- Embeds AceEvent into the target object making the functions from the mixins list available on target:..
+-- @param target target object to embed AceEvent in
+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
diff --git a/Libs/AceEvent-3.0/AceEvent-3.0.xml b/Libs/AceEvent-3.0/AceEvent-3.0.xml
new file mode 100644
index 0000000..9d02169
--- /dev/null
+++ b/Libs/AceEvent-3.0/AceEvent-3.0.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/Libs/AceGUI-3.0-SharedMediaWidgets/BackgroundWidget.lua b/Libs/AceGUI-3.0-SharedMediaWidgets/BackgroundWidget.lua
new file mode 100644
index 0000000..c6b2a12
--- /dev/null
+++ b/Libs/AceGUI-3.0-SharedMediaWidgets/BackgroundWidget.lua
@@ -0,0 +1,162 @@
+-- Widget is based on the AceGUIWidget-DropDown.lua supplied with AceGUI-3.0
+-- Widget created by Yssaril
+
+local AceGUI = LibStub("AceGUI-3.0")
+local Media = LibStub("LibSharedMedia-3.0")
+
+do
+ local min, max, floor = math.min, math.max, math.floor
+ local fixlevels = AceGUISharedMediaWidgets.fixlevels
+ local OnItemValueChanged = AceGUISharedMediaWidgets.OnItemValueChanged
+
+ do
+ local widgetType = "LSM30_Background_Item_Select"
+ local widgetVersion = 1
+
+ local function Frame_OnEnter(this)
+ local self = this.obj
+
+ if self.useHighlight then
+ self.highlight:Show()
+ self.texture:Show()
+ end
+ self:Fire("OnEnter")
+
+ if self.specialOnEnter then
+ self.specialOnEnter(self)
+ end
+ end
+
+ local function Frame_OnLeave(this)
+ local self = this.obj
+ self.texture:Hide()
+ self.highlight:Hide()
+ self:Fire("OnLeave")
+
+ if self.specialOnLeave then
+ self.specialOnLeave(self)
+ end
+ end
+
+ local function SetText(self, text)
+ if text and text ~= '' then
+ self.texture:SetTexture(Media:Fetch('background',text))
+ end
+ self.text:SetText(text or "")
+ end
+
+ local function Constructor()
+ local self = AceGUI:Create("Dropdown-Item-Toggle")
+ self.type = widgetType
+ self.SetText = SetText
+ local textureframe = CreateFrame('Frame')
+ textureframe:SetFrameStrata("TOOLTIP")
+ textureframe:SetWidth(128)
+ textureframe:SetHeight(128)
+ textureframe:SetPoint("LEFT",self.frame,"RIGHT",5,0)
+ self.textureframe = textureframe
+ local texture = textureframe:CreateTexture(nil, "OVERLAY")
+ texture:SetTexture(0,0,0,0)
+ texture:SetAllPoints(textureframe)
+ texture:Hide()
+ self.texture = texture
+ self.frame:SetScript("OnEnter", Frame_OnEnter)
+ self.frame:SetScript("OnLeave", Frame_OnLeave)
+ return self
+ end
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+ end
+
+ do
+ local widgetType = "LSM30_Background"
+ local widgetVersion = 3
+
+ local function Frame_OnEnter(this)
+ local self = this.obj
+ local text = self.text:GetText()
+ if text ~= nil and text ~= '' then
+ self.textureframe:Show()
+ end
+ end
+
+ local function Frame_OnLeave(this)
+ local self = this.obj
+ self.textureframe:Hide()
+ end
+
+ local function SetText(self, text)
+ if text and text ~= '' then
+ self.texture:SetTexture(Media:Fetch('background',text))
+ end
+ self.text:SetText(text or "")
+ end
+
+ local function AddListItem(self, value, text)
+ local item = AceGUI:Create("LSM30_Background_Item_Select")
+ item:SetText(text)
+ item.userdata.obj = self
+ item.userdata.value = value
+ item:SetCallback("OnValueChanged", OnItemValueChanged)
+ self.pullout:AddItem(item)
+ end
+
+ local function SetList(self, list)
+ self.list = list or Media:HashTable("background")
+ self.pullout:Clear()
+
+ if self.multiselect then
+ AddCloseButton()
+ end
+ end
+
+ local sortlist = {}
+ local function ParseListItems(self)
+ for v in pairs(self.list) do
+ sortlist[#sortlist + 1] = v
+ end
+ table.sort(sortlist)
+ for i, value in pairs(sortlist) do
+ AddListItem(self, value, value)
+ sortlist[i] = nil
+ end
+ end
+
+ local function Constructor()
+ local self = AceGUI:Create("Dropdown")
+ self.type = widgetType
+ self.SetText = SetText
+ self.SetList = SetList
+ self.SetValue = AceGUISharedMediaWidgets.SetValue
+
+ local left = _G[self.dropdown:GetName() .. "Left"]
+ local middle = _G[self.dropdown:GetName() .. "Middle"]
+ local right = _G[self.dropdown:GetName() .. "Right"]
+
+ local textureframe = CreateFrame('Frame')
+ textureframe:SetFrameStrata("TOOLTIP")
+ textureframe:SetWidth(128)
+ textureframe:SetHeight(128)
+ textureframe:SetPoint("LEFT",right,"RIGHT",-15,0)
+ self.textureframe = textureframe
+ local texture = textureframe:CreateTexture(nil, "OVERLAY")
+ texture:SetTexture(0,0,0,0)
+ texture:SetAllPoints(textureframe)
+ textureframe:Hide()
+ self.texture = texture
+
+ self.dropdown:EnableMouse(true)
+ self.dropdown:SetScript("OnEnter", Frame_OnEnter)
+ self.dropdown:SetScript("OnLeave", Frame_OnLeave)
+
+ local clickscript = self.button:GetScript("OnClick")
+ self.button:SetScript("OnClick", function(...)
+ self.pullout:Clear()
+ ParseListItems(self)
+ clickscript(...)
+ end)
+
+ return self
+ end
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+ end
+end
diff --git a/Libs/AceGUI-3.0-SharedMediaWidgets/BorderWidget.lua b/Libs/AceGUI-3.0-SharedMediaWidgets/BorderWidget.lua
new file mode 100644
index 0000000..aab9371
--- /dev/null
+++ b/Libs/AceGUI-3.0-SharedMediaWidgets/BorderWidget.lua
@@ -0,0 +1,163 @@
+-- Widget is based on the AceGUIWidget-DropDown.lua supplied with AceGUI-3.0
+-- Widget created by Yssaril
+
+local AceGUI = LibStub("AceGUI-3.0")
+local Media = LibStub("LibSharedMedia-3.0")
+
+do
+ local min, max, floor = math.min, math.max, math.floor
+ local fixlevels = AceGUISharedMediaWidgets.fixlevels
+ local OnItemValueChanged = AceGUISharedMediaWidgets.OnItemValueChanged
+
+ do
+ local widgetType = "LSM30_Border_Item_Select"
+ local widgetVersion = 1
+
+ local function Frame_OnEnter(this)
+ local self = this.obj
+
+ if self.useHighlight then
+ self.highlight:Show()
+ self.border:Show()
+ end
+ self:Fire("OnEnter")
+
+ if self.specialOnEnter then
+ self.specialOnEnter(self)
+ end
+ end
+
+ local function Frame_OnLeave(this)
+ local self = this.obj
+ self.border:Hide()
+ self.highlight:Hide()
+ self:Fire("OnLeave")
+
+ if self.specialOnLeave then
+ self.specialOnLeave(self)
+ end
+ end
+
+ local function SetText(self, text)
+ if text and text ~= '' then
+ local backdropTable = self.border:GetBackdrop()
+ backdropTable.edgeFile = Media:Fetch('border',text)
+ self.border:SetBackdrop(backdropTable)
+ end
+ self.text:SetText(text or "")
+ end
+
+ local function Constructor()
+ local self = AceGUI:Create("Dropdown-Item-Toggle")
+ self.type = widgetType
+ self.SetText = SetText
+ local border = CreateFrame('Frame')
+ border:SetFrameStrata("TOOLTIP")
+ border:SetWidth(64)
+ border:SetHeight(32)
+ border:SetPoint("LEFT",self.frame,"RIGHT",5,0)
+ border:SetBackdrop({bgFile = "Interface/Tooltips/UI-Tooltip-Background",
+ tile = true, tileSize = 16, edgeSize = 16,
+ insets = { left = 4, right = 4, top = 4, bottom = 4 }})
+ self.border = border
+ border:Hide()
+ self.frame:SetScript("OnEnter", Frame_OnEnter)
+ self.frame:SetScript("OnLeave", Frame_OnLeave)
+ return self
+ end
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+ end
+
+ do
+ local widgetType = "LSM30_Border"
+ local widgetVersion = 3
+
+ local function Frame_OnEnter(this)
+ local self = this.obj
+ local text = self.text:GetText()
+ if text ~= nil and text ~= '' then
+ self.borderframe:Show()
+ end
+ end
+
+ local function Frame_OnLeave(this)
+ local self = this.obj
+ self.borderframe:Hide()
+ end
+
+ local function AddListItem(self, value, text)
+ local item = AceGUI:Create("LSM30_Border_Item_Select")
+ item:SetText(text)
+ item.userdata.obj = self
+ item.userdata.value = value
+ item:SetCallback("OnValueChanged", OnItemValueChanged)
+ self.pullout:AddItem(item)
+ end
+
+ local function SetList(self, list)
+ self.list = list or Media:HashTable("border")
+ self.pullout:Clear()
+ if self.multiselect then
+ AddCloseButton()
+ end
+ end
+
+ local sortlist = {}
+ local function ParseListItems(self)
+ for v in pairs(self.list) do
+ sortlist[#sortlist + 1] = v
+ end
+ table.sort(sortlist)
+ for i, value in pairs(sortlist) do
+ AddListItem(self, value, value)
+ sortlist[i] = nil
+ end
+ end
+
+ local function SetText(self, text)
+ if text and text ~= '' then
+ local backdropTable = self.borderframe:GetBackdrop()
+ backdropTable.edgeFile = Media:Fetch('border',text)
+ self.borderframe:SetBackdrop(backdropTable)
+ end
+ self.text:SetText(text or "")
+ end
+
+ local function Constructor()
+ local self = AceGUI:Create("Dropdown")
+ self.type = widgetType
+ self.SetList = SetList
+ self.SetText = SetText
+ self.SetValue = AceGUISharedMediaWidgets.SetValue
+
+ local left = _G[self.dropdown:GetName() .. "Left"]
+ local middle = _G[self.dropdown:GetName() .. "Middle"]
+ local right = _G[self.dropdown:GetName() .. "Right"]
+
+ local borderframe = CreateFrame('Frame')
+ borderframe:SetFrameStrata("TOOLTIP")
+ borderframe:SetWidth(64)
+ borderframe:SetHeight(32)
+ borderframe:SetPoint("LEFT",right,"RIGHT",-15,0)
+ borderframe:SetBackdrop({bgFile = "Interface/Tooltips/UI-Tooltip-Background",
+ tile = true, tileSize = 16, edgeSize = 16,
+ insets = { left = 4, right = 4, top = 4, bottom = 4 }})
+ self.borderframe = borderframe
+ borderframe:Hide()
+
+ self.dropdown:EnableMouse(true)
+ self.dropdown:SetScript("OnEnter", Frame_OnEnter)
+ self.dropdown:SetScript("OnLeave", Frame_OnLeave)
+
+ local clickscript = self.button:GetScript("OnClick")
+ self.button:SetScript("OnClick", function(...)
+ self.pullout:Clear()
+ ParseListItems(self)
+ clickscript(...)
+ end)
+
+ return self
+ end
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+ end
+end
diff --git a/Libs/AceGUI-3.0-SharedMediaWidgets/FontWidget.lua b/Libs/AceGUI-3.0-SharedMediaWidgets/FontWidget.lua
new file mode 100644
index 0000000..b3232cf
--- /dev/null
+++ b/Libs/AceGUI-3.0-SharedMediaWidgets/FontWidget.lua
@@ -0,0 +1,94 @@
+-- Widget is based on the AceGUIWidget-DropDown.lua supplied with AceGUI-3.0
+-- Widget created by Yssaril
+
+local AceGUI = LibStub("AceGUI-3.0")
+local Media = LibStub("LibSharedMedia-3.0")
+
+do
+ local min, max, floor = math.min, math.max, math.floor
+ local fixlevels = AceGUISharedMediaWidgets.fixlevels
+ local OnItemValueChanged = AceGUISharedMediaWidgets.OnItemValueChanged
+
+ do
+ local widgetType = "LSM30_Font_Item_Select"
+ local widgetVersion = 1
+
+ local function SetText(self, text)
+ if text and text ~= '' then
+ local _, size, outline= self.text:GetFont()
+ self.text:SetFont(Media:Fetch('font',text),size,outline)
+ end
+ self.text:SetText(text or "")
+ end
+
+ local function Constructor()
+ local self = AceGUI:Create("Dropdown-Item-Toggle")
+ self.type = widgetType
+ self.SetText = SetText
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+ end
+
+ do
+ local widgetType = "LSM30_Font"
+ local widgetVersion = 3
+
+ local function SetText(self, text)
+ if text and text ~= '' then
+ local _, size, outline= self.text:GetFont()
+ self.text:SetFont(Media:Fetch('font',text),size,outline)
+ end
+ self.text:SetText(text or "")
+ end
+
+ local function AddListItem(self, value, text)
+ local item = AceGUI:Create("LSM30_Font_Item_Select")
+ item:SetText(text)
+ item.userdata.obj = self
+ item.userdata.value = value
+ item:SetCallback("OnValueChanged", OnItemValueChanged)
+ self.pullout:AddItem(item)
+ end
+
+ local function SetList(self, list)
+ self.list = list or Media:HashTable("font")
+ self.pullout:Clear()
+ if self.multiselect then
+ AddCloseButton()
+ end
+ end
+
+ local sortlist = {}
+ local function ParseListItems(self)
+ for v in pairs(self.list) do
+ sortlist[#sortlist + 1] = v
+ end
+ table.sort(sortlist)
+ for i, value in pairs(sortlist) do
+ AddListItem(self, value, value)
+ sortlist[i] = nil
+ end
+ end
+
+ local function Constructor()
+ local self = AceGUI:Create("Dropdown")
+ self.type = widgetType
+ self.SetText = SetText
+ self.SetValue = AceGUISharedMediaWidgets.SetValue
+ self.SetList = SetList
+
+ local clickscript = self.button:GetScript("OnClick")
+ self.button:SetScript("OnClick", function(...)
+ self.pullout:Clear()
+ ParseListItems(self)
+ clickscript(...)
+ end)
+
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+ end
+end
diff --git a/Libs/AceGUI-3.0-SharedMediaWidgets/SharedFunctions.lua b/Libs/AceGUI-3.0-SharedMediaWidgets/SharedFunctions.lua
new file mode 100644
index 0000000..5468e78
--- /dev/null
+++ b/Libs/AceGUI-3.0-SharedMediaWidgets/SharedFunctions.lua
@@ -0,0 +1,55 @@
+-- Widget is based on the AceGUIWidget-DropDown.lua supplied with AceGUI-3.0
+-- Widget created by Yssaril
+LoadAddOn("LibSharedMedia-3.0")
+local AceGUI = LibStub("AceGUI-3.0")
+local Media = LibStub("LibSharedMedia-3.0")
+
+AceGUISharedMediaWidgets = {}
+do
+ AceGUIWidgetLSMlists = {
+ ['font'] = Media:HashTable("font"),
+ ['sound'] = Media:HashTable("sound"),
+ ['statusbar'] = Media:HashTable("statusbar"),
+ ['border'] = Media:HashTable("border"),
+ ['background'] = Media:HashTable("background"),
+ }
+
+ local min, max, floor = math.min, math.max, math.floor
+
+ 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 function OnItemValueChanged(this, event, checked)
+ local self = this.userdata.obj
+ if self.multiselect then
+ self:Fire("OnValueChanged", this.userdata.value, checked)
+ else
+ if checked then
+ self:SetValue(this.userdata.value)
+ self:Fire("OnValueChanged", this.userdata.value)
+ else
+ this:SetValue(true)
+ end
+ self.pullout:Close()
+ end
+ end
+
+ local function SetValue(self, value)
+ if value then
+ self:SetText(value or "")
+ end
+ self.value = value
+ end
+
+ AceGUISharedMediaWidgets.fixlevels = fixlevels
+ AceGUISharedMediaWidgets.OnItemValueChanged = OnItemValueChanged
+ AceGUISharedMediaWidgets.SetValue = SetValue
+end
diff --git a/Libs/AceGUI-3.0-SharedMediaWidgets/SoundWidget.lua b/Libs/AceGUI-3.0-SharedMediaWidgets/SoundWidget.lua
new file mode 100644
index 0000000..79588f8
--- /dev/null
+++ b/Libs/AceGUI-3.0-SharedMediaWidgets/SoundWidget.lua
@@ -0,0 +1,274 @@
+-- Widget is based on the AceGUIWidget-DropDown.lua supplied with AceGUI-3.0
+-- Widget created by Yssaril
+
+local AceGUI = LibStub("AceGUI-3.0")
+local Media = LibStub("LibSharedMedia-3.0")
+
+do
+ local min, max, floor = math.min, math.max, math.floor
+ local fixlevels = AceGUISharedMediaWidgets.fixlevels
+ local OnItemValueChanged = AceGUISharedMediaWidgets.OnItemValueChanged
+
+ do
+ local widgetType = "LSM30_Sound_Item_Select"
+ local widgetVersion = 1
+
+ local function Frame_OnEnter(this)
+ local self = this.obj
+
+ if self.useHighlight then
+ self.highlight:Show()
+ end
+ self:Fire("OnEnter")
+
+ if self.specialOnEnter then
+ self.specialOnEnter(self)
+ end
+ end
+
+ local function Frame_OnLeave(this)
+ local self = this.obj
+
+ self.highlight:Hide()
+ self:Fire("OnLeave")
+
+ if self.specialOnLeave then
+ self.specialOnLeave(self)
+ end
+ end
+
+ local function OnAcquire(self)
+ self.frame:SetToplevel(true)
+ self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
+ end
+
+ local function OnRelease(self)
+ self.pullout = nil
+ self.frame:SetParent(nil)
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ end
+
+ local function SetPullout(self, pullout)
+ self.pullout = pullout
+
+ self.frame:SetParent(nil)
+ self.frame:SetParent(pullout.itemFrame)
+ self.parent = pullout.itemFrame
+ fixlevels(pullout.itemFrame, pullout.itemFrame:GetChildren())
+ end
+
+ local function SetText(self, text)
+ self.sound = text or ''
+ self.text:SetText(text or "")
+ end
+
+ local function GetText(self)
+ return self.text:GetText()
+ end
+
+ local function SetPoint(self, ...)
+ self.frame:SetPoint(...)
+ end
+
+ local function Show(self)
+ self.frame:Show()
+ end
+
+ local function Hide(self)
+ self.frame:Hide()
+ end
+
+ local function SetDisabled(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.useHighlight = false
+ self.text:SetTextColor(.5, .5, .5)
+ else
+ self.useHighlight = true
+ self.text:SetTextColor(1, 1, 1)
+ end
+ end
+
+ local function SetOnLeave(self, func)
+ self.specialOnLeave = func
+ end
+
+ local function SetOnEnter(self, func)
+ self.specialOnEnter = func
+ end
+
+ local function UpdateToggle(self)
+ if self.value then
+ self.check:Show()
+ else
+ self.check:Hide()
+ end
+ end
+
+ local function Frame_OnClick(this, button)
+ local self = this.obj
+ self.value = not self.value
+ UpdateToggle(self)
+ self:Fire("OnValueChanged", self.value)
+ end
+
+ local function Speaker_OnClick(this, button)
+ local self = this.obj
+ PlaySoundFile(Media:Fetch('sound',self.sound))
+ end
+
+ local function SetValue(self, value)
+ self.value = value
+ UpdateToggle(self)
+ end
+
+ local function Constructor()
+ local count = AceGUI:GetNextWidgetNum(type)
+ local frame = CreateFrame("Frame", "LSM30_Sound_DropDownItem"..count)
+ local self = {}
+ self.frame = frame
+ frame.obj = self
+ self.type = type
+
+ self.useHighlight = true
+
+ frame:SetHeight(17)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ local button = CreateFrame("Button", nil, frame)
+ button:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-22,0)
+ button:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0)
+ self.button = button
+ button.obj = self
+
+ local speakerbutton = CreateFrame("Button", nil, frame)
+ speakerbutton:SetWidth(16)
+ speakerbutton:SetHeight(16)
+ speakerbutton:SetPoint("RIGHT",frame,"RIGHT",-6,0)
+ self.speakerbutton = speakerbutton
+ speakerbutton.obj = self
+
+ local speaker = frame:CreateTexture(nil, "BACKGROUND")
+ speaker:SetTexture("Interface\\Common\\VoiceChat-Speaker")
+ speaker:SetAllPoints(speakerbutton)
+ self.speaker = speaker
+
+ local speakeron = speakerbutton:CreateTexture(nil, "HIGHLIGHT")
+ speakeron:SetTexture("Interface\\Common\\VoiceChat-On")
+ speakeron:SetAllPoints(speakerbutton)
+ self.speakeron = speakeron
+
+ local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
+ text:SetTextColor(1,1,1)
+ text:SetJustifyH("LEFT")
+ text:SetPoint("TOPLEFT",frame,"TOPLEFT",18,0)
+ text:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-24,0)
+ self.text = text
+
+ local highlight = button:CreateTexture(nil, "OVERLAY")
+ highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
+ highlight:SetBlendMode("ADD")
+ highlight:SetHeight(14)
+ highlight:ClearAllPoints()
+ highlight:SetPoint("RIGHT",frame,"RIGHT",-19,0)
+ highlight:SetPoint("LEFT",frame,"LEFT",5,0)
+ highlight:Hide()
+ self.highlight = highlight
+
+ local check = frame:CreateTexture("OVERLAY")
+ check:SetWidth(16)
+ check:SetHeight(16)
+ check:SetPoint("LEFT",frame,"LEFT",3,-1)
+ check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
+ check:Hide()
+ self.check = check
+
+ local sub = frame:CreateTexture("OVERLAY")
+ sub:SetWidth(16)
+ sub:SetHeight(16)
+ sub:SetPoint("RIGHT",frame,"RIGHT",-3,-1)
+ sub:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
+ sub:Hide()
+ self.sub = sub
+
+ button:SetScript("OnEnter", Frame_OnEnter)
+ button:SetScript("OnLeave", Frame_OnLeave)
+
+ self.OnAcquire = OnAcquire
+ self.OnRelease = OnRelease
+
+ self.SetPullout = SetPullout
+ self.GetText = GetText
+ self.SetText = SetText
+ self.SetDisabled = SetDisabled
+
+ self.SetPoint = SetPoint
+ self.Show = Show
+ self.Hide = Hide
+
+ self.SetOnLeave = SetOnLeave
+ self.SetOnEnter = SetOnEnter
+
+ self.button:SetScript("OnClick", Frame_OnClick)
+ self.speakerbutton:SetScript("OnClick", Speaker_OnClick)
+
+ self.SetValue = SetValue
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+ end
+
+ do
+ local widgetType = "LSM30_Sound"
+ local widgetVersion = 3
+
+ local function AddListItem(self, value, text)
+ local item = AceGUI:Create("LSM30_Sound_Item_Select")
+ item:SetText(text)
+ item.userdata.obj = self
+ item.userdata.value = value
+ item:SetCallback("OnValueChanged", OnItemValueChanged)
+ self.pullout:AddItem(item)
+ end
+
+ local function SetList(self, list)
+ self.list = list or Media:HashTable("sound")
+ self.pullout:Clear()
+ if self.multiselect then
+ AddCloseButton()
+ end
+ end
+
+ local sortlist = {}
+ local function ParseListItems(self)
+ for v in pairs(self.list) do
+ sortlist[#sortlist + 1] = v
+ end
+ table.sort(sortlist)
+ for i, value in pairs(sortlist) do
+ AddListItem(self, value, value)
+ sortlist[i] = nil
+ end
+ end
+
+ local function Constructor()
+ local self = AceGUI:Create("Dropdown")
+ self.type = widgetType
+ self.SetList = SetList
+ self.SetValue = AceGUISharedMediaWidgets.SetValue
+
+ local clickscript = self.button:GetScript("OnClick")
+ self.button:SetScript("OnClick", function(...)
+ self.pullout:Clear()
+ ParseListItems(self)
+ clickscript(...)
+ end)
+
+ return self
+ end
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+ end
+end
diff --git a/Libs/AceGUI-3.0-SharedMediaWidgets/StatusbarWidget.lua b/Libs/AceGUI-3.0-SharedMediaWidgets/StatusbarWidget.lua
new file mode 100644
index 0000000..433d3ed
--- /dev/null
+++ b/Libs/AceGUI-3.0-SharedMediaWidgets/StatusbarWidget.lua
@@ -0,0 +1,106 @@
+-- Widget is based on the AceGUIWidget-DropDown.lua supplied with AceGUI-3.0
+-- Widget created by Yssaril
+
+local AceGUI = LibStub("AceGUI-3.0")
+local Media = LibStub("LibSharedMedia-3.0")
+
+do
+ local min, max, floor = math.min, math.max, math.floor
+ local fixlevels = AceGUISharedMediaWidgets.fixlevels
+ local OnItemValueChanged = AceGUISharedMediaWidgets.OnItemValueChanged
+
+ do
+ local widgetType = "LSM30_Statusbar_Item_Select"
+ local widgetVersion = 1
+
+ local function SetText(self, text)
+ if text and text ~= '' then
+ self.texture:SetTexture(Media:Fetch('statusbar',text))
+ self.texture:SetVertexColor(.5,.5,.5)
+ end
+ self.text:SetText(text or "")
+ end
+
+ local function Constructor()
+ local self = AceGUI:Create("Dropdown-Item-Toggle")
+ self.type = widgetType
+ self.SetText = SetText
+ local texture = self.frame:CreateTexture(nil, "BACKGROUND")
+ texture:SetTexture(0,0,0,0)
+ texture:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",-4,1)
+ texture:SetPoint("TOPLEFT",self.frame,"TOPLEFT",6,-1)
+ self.texture = texture
+ return self
+ end
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+ end
+
+ do
+ local widgetType = "LSM30_Statusbar"
+ local widgetVersion = 3
+
+ local function SetText(self, text)
+ if text and text ~= '' then
+ self.texture:SetTexture(Media:Fetch('statusbar',text))
+ self.texture:SetVertexColor(.5,.5,.5)
+ end
+ self.text:SetText(text or "")
+ end
+
+ local function AddListItem(self, value, text)
+ local item = AceGUI:Create("LSM30_Statusbar_Item_Select")
+ item:SetText(text)
+ item.userdata.obj = self
+ item.userdata.value = value
+ item:SetCallback("OnValueChanged", OnItemValueChanged)
+ self.pullout:AddItem(item)
+ end
+
+ local function SetList(self, list)
+ self.list = list or Media:HashTable("statusbar")
+ self.pullout:Clear()
+ if self.multiselect then
+ AddCloseButton()
+ end
+ end
+
+ local sortlist = {}
+ local function ParseListItems(self)
+ for v in pairs(self.list) do
+ sortlist[#sortlist + 1] = v
+ end
+ table.sort(sortlist)
+ for i, value in pairs(sortlist) do
+ AddListItem(self, value, value)
+ sortlist[i] = nil
+ end
+ end
+
+ local function Constructor()
+ local self = AceGUI:Create("Dropdown")
+ self.type = widgetType
+ self.SetText = SetText
+ self.SetList = SetList
+ self.SetValue = AceGUISharedMediaWidgets.SetValue
+
+ local left = _G[self.dropdown:GetName() .. "Left"]
+ local middle = _G[self.dropdown:GetName() .. "Middle"]
+ local right = _G[self.dropdown:GetName() .. "Right"]
+
+ local texture = self.dropdown:CreateTexture(nil, "ARTWORK")
+ texture:SetPoint("BOTTOMRIGHT", right, "BOTTOMRIGHT" ,-39, 26)
+ texture:SetPoint("TOPLEFT", left, "TOPLEFT", 24, -24)
+ self.texture = texture
+
+ local clickscript = self.button:GetScript("OnClick")
+ self.button:SetScript("OnClick", function(...)
+ self.pullout:Clear()
+ ParseListItems(self)
+ clickscript(...)
+ end)
+
+ return self
+ end
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+ end
+end
diff --git a/Libs/AceGUI-3.0-SharedMediaWidgets/widget.xml b/Libs/AceGUI-3.0-SharedMediaWidgets/widget.xml
new file mode 100644
index 0000000..99572be
--- /dev/null
+++ b/Libs/AceGUI-3.0-SharedMediaWidgets/widget.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Libs/AceGUI-3.0/AceGUI-3.0.lua b/Libs/AceGUI-3.0/AceGUI-3.0.lua
new file mode 100644
index 0000000..f6c013c
--- /dev/null
+++ b/Libs/AceGUI-3.0/AceGUI-3.0.lua
@@ -0,0 +1,863 @@
+--- **AceGUI-3.0** provides access to numerous widgets which can be used to create GUIs.
+-- AceGUI is used by AceConfigDialog to create the option GUIs, but you can use it by itself
+-- to create any custom GUI. There are more extensive examples in the test suite in the Ace3
+-- stand-alone distribution.
+--
+-- **Note**: When using AceGUI-3.0 directly, please do not modify the frames of the widgets directly,
+-- as any "unknown" change to the widgets will cause addons that get your widget out of the widget pool
+-- to misbehave. If you think some part of a widget should be modifiable, please open a ticket, and we'll
+-- implement a proper API to modify it.
+-- @usage
+-- local AceGUI = LibStub("AceGUI-3.0")
+-- -- Create a container frame
+-- local f = AceGUI:Create("Frame")
+-- f:SetCallback("OnClose",function(widget) AceGUI:Release(widget) end)
+-- f:SetTitle("AceGUI-3.0 Example")
+-- f:SetStatusText("Status Bar")
+-- f:SetLayout("Flow")
+-- -- Create a button
+-- local btn = AceGUI:Create("Button")
+-- btn:SetWidth(170)
+-- btn:SetText("Button !")
+-- btn:SetCallback("OnClick", function() print("Click!") end)
+-- -- Add the button to the container
+-- f:AddChild(btn)
+-- @class file
+-- @name AceGUI-3.0
+-- @release $Id: AceGUI-3.0.lua 896 2009-12-06 16:29:49Z nevcairiel $
+local ACEGUI_MAJOR, ACEGUI_MINOR = "AceGUI-3.0", 30
+local AceGUI, oldminor = LibStub:NewLibrary(ACEGUI_MAJOR, ACEGUI_MINOR)
+
+if not AceGUI then return end -- No upgrade needed
+
+-- Lua APIs
+local tconcat, tremove, tinsert = table.concat, table.remove, table.insert
+local select, pairs, next, type = select, pairs, next, type
+local error, assert, loadstring = error, assert, loadstring
+local setmetatable, rawget, rawset = setmetatable, rawget, rawset
+local math_max = math.max
+
+-- WoW APIs
+local UIParent = UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: geterrorhandler, LibStub
+
+--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
+
+--[[
+ 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, ...)
+ return Dispatchers[select('#', ...)](func, ...)
+end
+
+-- Recycling functions
+local newWidget, delWidget
+do
+ -- Version Upgrade in Minor 29
+ -- Internal Storage of the objects changed, from an array table
+ -- to a hash table, and additionally we introduced versioning on
+ -- the widgets which would discard all widgets from a pre-29 version
+ -- anyway, so we just clear the storage now, and don't try to
+ -- convert the storage tables to the new format.
+ -- This should generally not cause *many* widgets to end up in trash,
+ -- since once dialogs are opened, all addons should be loaded already
+ -- and AceGUI should be on the latest version available on the users
+ -- setup.
+ -- -- nevcairiel - Nov 2nd, 2009
+ if oldminor and oldminor < 29 and AceGUI.objPools then
+ AceGUI.objPools = nil
+ end
+
+ 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 newWidget(type)
+ if not WidgetRegistry[type] then
+ error("Attempt to instantiate unknown widget type", 2)
+ end
+
+ if not objPools[type] then
+ objPools[type] = {}
+ end
+
+ local newObj = next(objPools[type])
+ if not newObj then
+ newObj = WidgetRegistry[type]()
+ newObj.AceGUIWidgetVersion = WidgetVersions[type]
+ else
+ objPools[type][newObj] = nil
+ -- if the widget is older then the latest, don't even try to reuse it
+ -- just forget about it, and grab a new one.
+ if not newObj.AceGUIWidgetVersion or newObj.AceGUIWidgetVersion < WidgetVersions[type] then
+ return newWidget(type)
+ end
+ end
+ return newObj
+ end
+ -- Releases an instance to the Pool
+ function delWidget(obj,type)
+ if not objPools[type] then
+ objPools[type] = {}
+ end
+ if objPools[type][obj] then
+ error("Attempt to Release Widget that is already released", 2)
+ end
+ objPools[type][obj] = true
+ end
+end
+
+
+-------------------
+-- API Functions --
+-------------------
+
+-- Gets a widget Object
+
+--- Create a new Widget of the given type.
+-- This function will instantiate a new widget (or use one from the widget pool), and call the
+-- OnAcquire function on it, before returning.
+-- @param type The type of the widget.
+-- @return The newly created widget.
+function AceGUI:Create(type)
+ if WidgetRegistry[type] then
+ local widget = newWidget(type)
+
+ if rawget(widget,'Acquire') then
+ widget.OnAcquire = widget.Acquire
+ widget.Acquire = nil
+ elseif rawget(widget,'Aquire') then
+ widget.OnAcquire = widget.Aquire
+ widget.Aquire = nil
+ end
+
+ if rawget(widget,'Release') then
+ widget.OnRelease = rawget(widget,'Release')
+ widget.Release = nil
+ end
+
+ if widget.OnAcquire then
+ widget:OnAcquire()
+ else
+ error(("Widget type %s doesn't supply an OnAcquire Function"):format(type))
+ end
+ -- Set the default Layout ('List')
+ safecall(widget.SetLayout, widget, 'List')
+ safecall(widget.ResumeLayout, widget)
+ return widget
+ end
+end
+
+--- Releases a widget Object.
+-- This function calls OnRelease on the widget and places it back in the widget pool.
+-- Any data on the widget is being erased, and the widget will be hidden.\\
+-- If this widget is a Container-Widget, all of its Child-Widgets will be releases as well.
+-- @param widget The widget to release
+function AceGUI:Release(widget)
+ safecall( widget.PauseLayout, widget )
+ widget:Fire("OnRelease")
+ safecall( widget.ReleaseChildren, widget )
+
+ if widget.OnRelease then
+ widget:OnRelease()
+ else
+ error(("Widget type %s doesn't supply an OnRelease Function"):format(type))
+ end
+ 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.relWidth = nil
+ widget.height = nil
+ widget.relHeight = nil
+ widget.noAutoHeight = nil
+ widget.frame:ClearAllPoints()
+ widget.frame:Hide()
+ widget.frame:SetParent(UIParent)
+ widget.frame.width = nil
+ widget.frame.height = nil
+ if widget.content then
+ widget.content.width = nil
+ widget.content.height = nil
+ end
+ delWidget(widget, widget.type)
+end
+
+-----------
+-- Focus --
+-----------
+
+
+--- Called when a widget has taken focus.
+-- e.g. Dropdowns opening, Editboxes gaining kb focus
+-- @param widget The widget that should be focused
+function AceGUI:SetFocus(widget)
+ if self.FocusedWidget and self.FocusedWidget ~= widget then
+ safecall(self.FocusedWidget.ClearFocus, self.FocusedWidget)
+ end
+ self.FocusedWidget = widget
+end
+
+
+--- Called when something has happened that could cause widgets with focus to drop it
+-- e.g. titlebar of a frame being clicked
+function AceGUI:ClearFocus()
+ if self.FocusedWidget then
+ safecall(self.FocusedWidget.ClearFocus, self.FocusedWidget)
+ self.FocusedWidget = nil
+ end
+end
+
+-------------
+-- Widgets --
+-------------
+--[[
+ Widgets must provide the following functions
+ OnAcquire() - Called when the object is acquired, should set everything to a default hidden state
+ OnRelease() - 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
+ :OnWidthSet(width) - Called when the width of the widget is changed
+ :OnHeightSet(height) - Called when the height of the widget is changed
+ Widgets should not use the OnSizeChanged events of thier frame or content members, use these methods instead
+ AceGUI already sets a handler to the event
+ :LayoutFinished(width, height) - called after a layout has finished, the width and height will be the width and height of the
+ area used for controls. These can be nil if the layout used the existing size to layout the controls.
+
+]]
+
+--------------------------
+-- 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.SetRelativeWidth = function(self, width)
+ if width <= 0 or width > 1 then
+ error(":SetRelativeWidth(width): Invalid relative width.", 2)
+ end
+ self.relWidth = width
+ self.width = "relative"
+ end
+
+ WidgetBase.SetHeight = function(self, height)
+ self.frame:SetHeight(height)
+ self.frame.height = height
+ if self.OnHeightSet then
+ self:OnHeightSet(height)
+ end
+ end
+
+ --[[ WidgetBase.SetRelativeHeight = function(self, height)
+ if height <= 0 or height > 1 then
+ error(":SetRelativeHeight(height): Invalid relative height.", 2)
+ end
+ self.relHeight = height
+ self.height = "relative"
+ end ]]
+
+ WidgetBase.IsVisible = function(self)
+ return self.frame:IsVisible()
+ end
+
+ WidgetBase.IsShown= function(self)
+ return self.frame:IsShown()
+ end
+
+ WidgetBase.Release = function(self)
+ AceGUI:Release(self)
+ end
+
+ WidgetBase.SetPoint = function(self, ...)
+ return self.frame:SetPoint(...)
+ end
+
+ WidgetBase.ClearAllPoints = function(self)
+ return self.frame:ClearAllPoints()
+ end
+
+ WidgetBase.GetNumPoints = function(self)
+ return self.frame:GetNumPoints()
+ end
+
+ WidgetBase.GetPoint = function(self, ...)
+ return self.frame:GetPoint(...)
+ end
+
+ WidgetBase.GetUserDataTable = function(self)
+ return self.userdata
+ end
+
+ WidgetBase.SetUserData = function(self, key, value)
+ self.userdata[key] = value
+ end
+
+ WidgetBase.GetUserData = function(self, key)
+ return self.userdata[key]
+ end
+
+ WidgetBase.IsFullHeight = function(self)
+ return self.height == "fill"
+ end
+
+ WidgetBase.SetFullHeight = function(self, isFull)
+ if isFull then
+ self.height = "fill"
+ else
+ self.height = nil
+ end
+ end
+
+ WidgetBase.IsFullWidth = function(self)
+ return self.width == "fill"
+ end
+
+ WidgetBase.SetFullWidth = function(self, isFull)
+ if isFull then
+ self.width = "fill"
+ else
+ self.width = nil
+ 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, beforeWidget)
+ if beforeWidget then
+ local siblingIndex = 1
+ for _, widget in pairs(self.children) do
+ if widget == beforeWidget then
+ break
+ end
+ siblingIndex = siblingIndex + 1
+ end
+ tinsert(self.children, siblingIndex, child)
+ else
+ tinsert(self.children, child)
+ end
+ child:SetParent(self)
+ child.frame:Show()
+ self:DoLayout()
+ end
+
+ WidgetContainerBase.AddChildren = function(self, ...)
+ for i = 1, select("#", ...) do
+ local child = select(i, ...)
+ tinsert(self.children, child)
+ child:SetParent(self)
+ child.frame:Show()
+ end
+ self:DoLayout()
+ end
+
+ WidgetContainerBase.ReleaseChildren = function(self)
+ local children = self.children
+ for i = 1,#children do
+ AceGUI:Release(children[i])
+ children[i] = nil
+ end
+ end
+
+ WidgetContainerBase.SetLayout = function(self, Layout)
+ self.LayoutFunc = AceGUI:GetLayout(Layout)
+ end
+
+ WidgetContainerBase.SetAutoAdjustHeight = function(self, adjust)
+ if adjust then
+ self.noAutoHeight = nil
+ else
+ self.noAutoHeight = true
+ end
+ end
+
+ local function FrameResize(this)
+ local self = this.obj
+ if this:GetWidth() and this:GetHeight() then
+ if self.OnWidthSet then
+ self:OnWidthSet(this:GetWidth())
+ end
+ if self.OnHeightSet then
+ self:OnHeightSet(this:GetHeight())
+ end
+ end
+ end
+
+ local function ContentResize(this)
+ if this:GetWidth() and this:GetHeight() 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
+
+ --- Register a widget-class as a container for newly created widgets.
+ -- @param widget The widget class
+ function AceGUI:RegisterAsContainer(widget)
+ widget.children = {}
+ widget.userdata = {}
+ widget.events = {}
+ widget.base = WidgetContainerBase
+ widget.content.obj = widget
+ widget.frame.obj = widget
+ widget.content:SetScript("OnSizeChanged",ContentResize)
+ widget.frame:SetScript("OnSizeChanged",FrameResize)
+ setmetatable(widget,{__index=WidgetContainerBase})
+ widget:SetLayout("List")
+ end
+
+ --- Register a widget-class as a widget.
+ -- @param widget The widget class
+ function AceGUI:RegisterAsWidget(widget)
+ widget.userdata = {}
+ widget.events = {}
+ widget.base = WidgetBase
+ widget.frame.obj = widget
+ widget.frame:SetScript("OnSizeChanged",FrameResize)
+ setmetatable(widget,{__index=WidgetBase})
+ end
+end
+
+
+
+
+------------------
+-- Widget API --
+------------------
+
+--- Registers a widget Constructor, this function returns a new instance of the Widget
+-- @param Name The name of the widget
+-- @param Constructor The widget constructor function
+-- @param Version The version 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
+-- @param Name The name of the layout
+-- @param LayoutFunc Reference to the layout function
+function AceGUI:RegisterLayout(Name, LayoutFunc)
+ assert(type(LayoutFunc) == "function")
+ if type(Name) == "string" then
+ Name = Name:upper()
+ end
+ LayoutRegistry[Name] = LayoutFunc
+end
+
+--- Get a Layout Function from the registry
+-- @param Name The name of the layout
+function AceGUI:GetLayout(Name)
+ if type(Name) == "string" then
+ Name = Name:upper()
+ end
+ return LayoutRegistry[Name]
+end
+
+AceGUI.counts = AceGUI.counts or {}
+
+--- A type-based counter to count the number of widgets created.
+-- This is used by widgets that require a named frame, e.g. when a Blizzard
+-- Template requires it.
+-- @param type The widget type
+function AceGUI:GetNextWidgetNum(type)
+ if not self.counts[type] then
+ self.counts[type] = 0
+ end
+ self.counts[type] = self.counts[type] + 1
+ return self.counts[type]
+end
+
+--[[ Widget Template
+
+--------------------------
+-- Widget Name --
+--------------------------
+do
+ local Type = "Type"
+
+ local function OnAcquire(self)
+
+ end
+
+ local function OnRelease(self)
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ end
+
+
+ local function Constructor()
+ local frame = CreateFrame("Frame",nil,UIParent)
+ local self = {}
+ self.type = Type
+
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+
+ 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
+ local width = content.width or content:GetWidth() or 0
+ for i = 1, #children do
+ local child = children[i]
+
+ 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
+ child:SetWidth(width)
+ frame:SetPoint("RIGHT",content,"RIGHT")
+ if child.OnWidthSet then
+ child:OnWidthSet(content.width or content:GetWidth())
+ end
+ if child.DoLayout then
+ child:DoLayout()
+ end
+ elseif child.width == "relative" then
+ child:SetWidth(width * child.relWidth)
+ 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]:SetWidth(content:GetWidth() or 0)
+ children[1]:SetHeight(content:GetHeight() or 0)
+ 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
+ local oversize
+ for i = 1, #children do
+ local child = children[i]
+ oversize = nil
+ local frame = child.frame
+ local frameheight = frame.height or frame:GetHeight() or 0
+ local framewidth = frame.width or frame:GetWidth() or 0
+ lastframeoffset = frameoffset
+ -- HACK: Why did we set a frameoffset of (frameheight / 2) ?
+ -- That was moving all widgets half the widgets size down, is that intended?
+ -- Actually, it seems to be neccessary for many cases, we'll leave it in for now.
+ -- If widgets seem to anchor weirdly with this, provide a valid alignoffset for them.
+ -- TODO: Investigate moar!
+ frameoffset = child.alignoffset or (frameheight / 2)
+
+ if child.width == "relative" then
+ framewidth = width * child.relWidth
+ end
+
+ 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
+ if usedwidth > width then
+ oversize = true
+ end
+ 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
+ if isfullheight then
+ -- a previous row has already filled the entire height, there's nothing we can usefully do anymore
+ -- (maybe error/warn about this?)
+ break
+ end
+ --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 = framewidth
+ if usedwidth > width then
+ oversize = true
+ end
+ -- 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))
+ --print("type:", child.type, "offset:",frameoffset-lastframeoffset)
+ 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.OnWidthSet then
+ child:OnWidthSet(width)
+ end
+ if child.DoLayout then
+ child:DoLayout()
+ end
+ rowheight = frame.height or frame:GetHeight() or 0
+ rowoffset = child.alignoffset or (rowheight / 2)
+ rowstartoffset = rowoffset
+ elseif child.width == "relative" then
+ child:SetWidth(width * child.relWidth)
+
+ if child.OnWidthSet then
+ child:OnWidthSet(width)
+ end
+
+ if child.DoLayout then
+ child:DoLayout()
+ end
+ elseif oversize then
+ if width > 1 then
+ frame:SetPoint("RIGHT",content,"RIGHT",0,0)
+ end
+ end
+
+ if child.height == "fill" then
+ frame:SetPoint("BOTTOM",content,"BOTTOM")
+ isfullheight = true
+ 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
+ )
diff --git a/Libs/AceGUI-3.0/AceGUI-3.0.xml b/Libs/AceGUI-3.0/AceGUI-3.0.xml
new file mode 100644
index 0000000..65b0000
--- /dev/null
+++ b/Libs/AceGUI-3.0/AceGUI-3.0.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-BlizOptionsGroup.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-BlizOptionsGroup.lua
new file mode 100644
index 0000000..1c16d4f
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-BlizOptionsGroup.lua
@@ -0,0 +1,153 @@
+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
+]]
+
+-- WoW APIs
+local CreateFrame = CreateFrame
+
+do
+ local Type = "BlizOptionsGroup"
+ local Version = 10
+
+ local function OnAcquire(self)
+
+ end
+
+ local function OnRelease(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",10,-10)
+ self.label:SetText("")
+ else
+ content:SetPoint("TOPLEFT",self.frame,"TOPLEFT",10,-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.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+ 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", 10, -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
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Button.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Button.lua
new file mode 100644
index 0000000..f9cc382
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Button.lua
@@ -0,0 +1,104 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- WoW APIs
+local _G = _G
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+--------------------------
+-- Button --
+--------------------------
+do
+ local Type = "Button"
+ local Version = 12
+
+ local function OnAcquire(self)
+ -- restore default values
+ self:SetHeight(24)
+ self:SetWidth(200)
+ end
+
+ local function OnRelease(self)
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ self:SetDisabled(false)
+ end
+
+ local function Button_OnClick(this, ...)
+ this.obj:Fire("OnClick", ...)
+ AceGUI:ClearFocus()
+ 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 function Constructor()
+ local num = AceGUI:GetNextWidgetNum(Type)
+ local name = "AceGUI30Button"..num
+ local frame = CreateFrame("Button",name,UIParent,"UIPanelButtonTemplate2")
+ local self = {}
+ self.num = num
+ self.type = Type
+ self.frame = frame
+
+ local left = _G[name .. "Left"]
+ local right = _G[name .. "Right"]
+ local middle = _G[name .. "Middle"]
+
+ left:SetPoint("TOP", frame, "TOP", 0, 0)
+ left:SetPoint("BOTTOM", frame, "BOTTOM", 0, 0)
+
+ right:SetPoint("TOP", frame, "TOP", 0, 0)
+ right:SetPoint("BOTTOM", frame, "BOTTOM", 0, 0)
+
+ middle:SetPoint("TOP", frame, "TOP", 0, 0)
+ middle:SetPoint("BOTTOM", frame, "BOTTOM", 0, 0)
+
+ local text = frame:GetFontString()
+ self.text = text
+ text:ClearAllPoints()
+ text:SetPoint("TOPLEFT",frame,"TOPLEFT", 15, -1)
+ text:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT", -15, 1)
+ text:SetJustifyV("MIDDLE")
+
+ 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.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+
+ self.frame = frame
+ frame.obj = self
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(Type,Constructor,Version)
+end
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-CheckBox.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-CheckBox.lua
new file mode 100644
index 0000000..f29b710
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-CheckBox.lua
@@ -0,0 +1,300 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local select = select
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: SetDesaturation, GameFontHighlight
+
+--------------------------
+-- Check Box --
+--------------------------
+--[[
+ Events :
+ OnValueChanged
+
+]]
+do
+ local Type = "CheckBox"
+ local Version = 13
+
+ local function OnAcquire(self)
+ self:SetValue(false)
+ self.tristate = nil
+ self:SetHeight(24)
+ self:SetWidth(200)
+ self:SetImage()
+ end
+
+ local function OnRelease(self)
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ self.check:Hide()
+ self.highlight:Hide()
+ self.down = nil
+ self.checked = nil
+ self:SetType()
+ self:SetDisabled(false)
+ self:SetDescription(nil)
+ end
+
+ local function CheckBox_OnEnter(this)
+ local self = this.obj
+ self.highlight:Show()
+ self:Fire("OnEnter")
+ end
+
+ local function CheckBox_OnLeave(this)
+ local self = this.obj
+ self.highlight:Hide()
+ 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
+ AceGUI:ClearFocus()
+ end
+
+ local function SetDisabled(self,disabled)
+ self.disabled = disabled
+ if disabled then
+ self.frame:Disable()
+ self.text:SetTextColor(0.5,0.5,0.5)
+ SetDesaturation(self.check, true)
+ else
+ self.frame:Enable()
+ 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)
+ self.check:Show()
+ else
+ --Nil is the unknown tristate value
+ if self.tristate and value == nil then
+ SetDesaturation(self.check, true)
+ self.check:Show()
+ else
+ SetDesaturation(self.check, false)
+ 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:SetHeight(16)
+ checkbg:SetWidth(16)
+ checkbg:SetTexture("Interface\\Buttons\\UI-RadioButton")
+ checkbg:SetTexCoord(0,0.25,0,1)
+ check:SetHeight(16)
+ check:SetWidth(16)
+ check:SetTexture("Interface\\Buttons\\UI-RadioButton")
+ check:SetTexCoord(0.25,0.5,0,1)
+ check:SetBlendMode("ADD")
+ highlight:SetTexture("Interface\\Buttons\\UI-RadioButton")
+ highlight:SetTexCoord(0.5,0.75,0,1)
+ else
+ checkbg:SetHeight(24)
+ checkbg:SetWidth(24)
+ checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up")
+ checkbg:SetTexCoord(0,1,0,1)
+ check:SetHeight(24)
+ check:SetWidth(24)
+ 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 SetDescription(self, desc)
+ if desc then
+ if not self.desc then
+ local desc = self.frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
+ desc:ClearAllPoints()
+ desc:SetPoint("TOPLEFT", self.check, "TOPRIGHT", 5, -21)
+ desc:SetWidth(self.frame.width - 30)
+ desc:SetJustifyH("LEFT")
+ desc:SetJustifyV("TOP")
+ self.desc = desc
+ end
+ self.desc:Show()
+ --self.text:SetFontObject(GameFontNormal)
+ self.desc:SetText(desc)
+ self:SetHeight(28 + self.desc:GetHeight())
+ else
+ if self.desc then
+ self.desc:SetText("")
+ self.desc:Hide()
+ end
+ self.text:SetFontObject(GameFontHighlight)
+ self:SetHeight(24)
+ end
+ end
+
+ local function SetImage(self, path, ...)
+ local image = self.image
+ image:SetTexture(path)
+
+ if image:GetTexture() then
+ local n = select('#', ...)
+ if n == 4 or n == 8 then
+ image:SetTexCoord(...)
+ else
+ image:SetTexCoord(0, 1, 0, 1)
+ end
+ end
+ self:AlignImage()
+ end
+
+ local function AlignImage(self)
+ local img = self.image:GetTexture()
+ self.text:ClearAllPoints()
+ if not img then
+ self.text:SetPoint("LEFT", self.check, "RIGHT", 0, 0)
+ self.text:SetPoint("RIGHT", self.frame, "RIGHT", 0, 0)
+ else
+ self.text:SetPoint("LEFT", self.image,"RIGHT", 1, 0)
+ self.text:SetPoint("RIGHT", self.frame,"RIGHT", 0, 0)
+ end
+ end
+
+ local function OnWidthSet(self, width)
+ if self.desc and self.desc:GetText() ~= "" then
+ self.desc:SetWidth(width - 30)
+ self:SetHeight(28 + self.desc:GetHeight())
+ end
+ end
+
+ local function Constructor()
+ local frame = CreateFrame("Button",nil,UIParent)
+ local self = {}
+ self.type = Type
+
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+
+ self.SetValue = SetValue
+ self.GetValue = GetValue
+ self.SetDisabled = SetDisabled
+ self.SetType = SetType
+ self.ToggleChecked = ToggleChecked
+ self.SetLabel = SetLabel
+ self.SetTriState = SetTriState
+ self.SetDescription = SetDescription
+ self.OnWidthSet = OnWidthSet
+ self.SetImage = SetImage
+ self.AlignImage = AlignImage
+
+ 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("TOPLEFT",frame,"TOPLEFT",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, "OVERLAY")
+ self.highlight = highlight
+ highlight:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight")
+ highlight:SetBlendMode("ADD")
+ highlight:SetAllPoints(checkbg)
+ highlight:Hide()
+
+ local image = frame:CreateTexture(nil, "OVERLAY")
+ self.image = image
+ image:SetHeight(16)
+ image:SetWidth(16)
+ image:SetPoint("LEFT", check, "RIGHT", 1, 0)
+
+ 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
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-ColorPicker.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-ColorPicker.lua
new file mode 100644
index 0000000..c3e363d
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-ColorPicker.lua
@@ -0,0 +1,181 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: ShowUIPanel, HideUIPanel, ColorPickerFrame, OpacitySliderFrame
+
+--------------------------
+-- ColorPicker --
+--------------------------
+do
+ local Type = "ColorPicker"
+ local Version = 11
+
+ local function OnAcquire(self)
+ self.HasAlpha = false
+ self:SetColor(0,0,0,1)
+ self:SetHeight(24)
+ self:SetWidth(200)
+ 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
+ AceGUI:ClearFocus()
+ end
+
+ local function OnRelease(self)
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ end
+
+ local function SetDisabled(self, disabled)
+ self.disabled = disabled
+ if self.disabled then
+ self.frame:Disable()
+ self.text:SetTextColor(0.5,0.5,0.5)
+ else
+ self.frame:Enable()
+ self.text:SetTextColor(1,1,1)
+ end
+ end
+
+ local function Constructor()
+ local frame = CreateFrame("Button",nil,UIParent)
+ local self = {}
+ self.type = Type
+
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+
+ 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
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown-Items.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown-Items.lua
new file mode 100644
index 0000000..16b0db7
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown-Items.lua
@@ -0,0 +1,459 @@
+--[[ $Id: AceGUIWidget-DropDown-Items.lua 877 2009-11-02 15:56:50Z nevcairiel $ ]]--
+
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local select, assert = select, assert
+
+-- WoW APIs
+local CreateFrame = CreateFrame
+
+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 function fixstrata(strata, parent, ...)
+ local i = 1
+ local child = select(i, ...)
+ parent:SetFrameStrata(strata)
+ while child do
+ fixstrata(strata, child, child:GetChildren())
+ i = i + 1
+ child = select(i, ...)
+ end
+end
+
+-- ItemBase is the base "class" for all dropdown items.
+-- Each item has to use ItemBase.Create(widgetType) to
+-- create an initial 'self' value.
+-- ItemBase will add common functions and ui event handlers.
+-- Be sure to keep basic usage when you override functions.
+
+local ItemBase = {
+ -- NOTE: The ItemBase version is added to each item's version number
+ -- to ensure proper updates on ItemBase changes.
+ -- Use at least 1000er steps.
+ version = 1000,
+ counter = 0,
+}
+
+function ItemBase.Frame_OnEnter(this)
+ local self = this.obj
+
+ if self.useHighlight then
+ self.highlight:Show()
+ end
+ self:Fire("OnEnter")
+
+ if self.specialOnEnter then
+ self.specialOnEnter(self)
+ end
+end
+
+function ItemBase.Frame_OnLeave(this)
+ local self = this.obj
+
+ self.highlight:Hide()
+ self:Fire("OnLeave")
+
+ if self.specialOnLeave then
+ self.specialOnLeave(self)
+ end
+end
+
+-- exported, AceGUI callback
+function ItemBase.OnAcquire(self)
+ self.frame:SetToplevel(true)
+ self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
+end
+
+-- exported, AceGUI callback
+function ItemBase.OnRelease(self)
+ self:SetDisabled(false)
+ self.pullout = nil
+ self.frame:SetParent(nil)
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+end
+
+-- exported
+-- NOTE: this is called by a Dropdown-Pullout.
+-- Do not call this method directly
+function ItemBase.SetPullout(self, pullout)
+ self.pullout = pullout
+
+ self.frame:SetParent(nil)
+ self.frame:SetParent(pullout.itemFrame)
+ self.parent = pullout.itemFrame
+ fixlevels(pullout.itemFrame, pullout.itemFrame:GetChildren())
+end
+
+-- exported
+function ItemBase.SetText(self, text)
+ self.text:SetText(text or "")
+end
+
+-- exported
+function ItemBase.GetText(self)
+ return self.text:GetText()
+end
+
+-- exported
+function ItemBase.SetPoint(self, ...)
+ self.frame:SetPoint(...)
+end
+
+-- exported
+function ItemBase.Show(self)
+ self.frame:Show()
+end
+
+-- exported
+function ItemBase.Hide(self)
+ self.frame:Hide()
+end
+
+-- exported
+function ItemBase.SetDisabled(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.useHighlight = false
+ self.text:SetTextColor(.5, .5, .5)
+ else
+ self.useHighlight = true
+ self.text:SetTextColor(1, 1, 1)
+ end
+end
+
+-- exported
+-- NOTE: this is called by a Dropdown-Pullout.
+-- Do not call this method directly
+function ItemBase.SetOnLeave(self, func)
+ self.specialOnLeave = func
+end
+
+-- exported
+-- NOTE: this is called by a Dropdown-Pullout.
+-- Do not call this method directly
+function ItemBase.SetOnEnter(self, func)
+ self.specialOnEnter = func
+end
+
+function ItemBase.Create(type)
+ -- NOTE: Most of the following code is copied from AceGUI-3.0/Dropdown widget
+ local count = AceGUI:GetNextWidgetNum(type)
+ local frame = CreateFrame("Button", "AceGUI30DropDownItem"..count)
+ local self = {}
+ self.frame = frame
+ frame.obj = self
+ self.type = type
+
+ self.useHighlight = true
+
+ frame:SetHeight(17)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
+ text:SetTextColor(1,1,1)
+ text:SetJustifyH("LEFT")
+ text:SetPoint("TOPLEFT",frame,"TOPLEFT",18,0)
+ text:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-8,0)
+ self.text = text
+
+ 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()
+ self.highlight = highlight
+
+ local check = frame:CreateTexture("OVERLAY")
+ check:SetWidth(16)
+ check:SetHeight(16)
+ check:SetPoint("LEFT",frame,"LEFT",3,-1)
+ check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
+ check:Hide()
+ self.check = check
+
+ local sub = frame:CreateTexture("OVERLAY")
+ sub:SetWidth(16)
+ sub:SetHeight(16)
+ sub:SetPoint("RIGHT",frame,"RIGHT",-3,-1)
+ sub:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
+ sub:Hide()
+ self.sub = sub
+
+ frame:SetScript("OnEnter", ItemBase.Frame_OnEnter)
+ frame:SetScript("OnLeave", ItemBase.Frame_OnLeave)
+
+ self.OnAcquire = ItemBase.OnAcquire
+ self.OnRelease = ItemBase.OnRelease
+
+ self.SetPullout = ItemBase.SetPullout
+ self.GetText = ItemBase.GetText
+ self.SetText = ItemBase.SetText
+ self.SetDisabled = ItemBase.SetDisabled
+
+ self.SetPoint = ItemBase.SetPoint
+ self.Show = ItemBase.Show
+ self.Hide = ItemBase.Hide
+
+ self.SetOnLeave = ItemBase.SetOnLeave
+ self.SetOnEnter = ItemBase.SetOnEnter
+
+ return self
+end
+
+--[[
+ Template for items:
+
+-- Item:
+--
+do
+ local widgetType = "Dropdown-Item-"
+ local widgetVersion = 1
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
+--]]
+
+-- Item: Header
+-- A single text entry.
+-- Special: Different text color and no highlight
+do
+ local widgetType = "Dropdown-Item-Header"
+ local widgetVersion = 1
+
+ local function OnEnter(this)
+ local self = this.obj
+ self:Fire("OnEnter")
+
+ if self.specialOnEnter then
+ self.specialOnEnter(self)
+ end
+ end
+
+ local function OnLeave(this)
+ local self = this.obj
+ self:Fire("OnLeave")
+
+ if self.specialOnLeave then
+ self.specialOnLeave(self)
+ end
+ end
+
+ -- exported, override
+ local function SetDisabled(self, disabled)
+ ItemBase.SetDisabled(self, disabled)
+ if not disabled then
+ self.text:SetTextColor(1, 1, 0)
+ end
+ end
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ self.SetDisabled = SetDisabled
+
+ self.frame:SetScript("OnEnter", OnEnter)
+ self.frame:SetScript("OnLeave", OnLeave)
+
+ self.text:SetTextColor(1, 1, 0)
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
+
+-- Item: Execute
+-- A simple button
+do
+ local widgetType = "Dropdown-Item-Execute"
+ local widgetVersion = 1
+
+ local function Frame_OnClick(this, button)
+ local self = this.obj
+ if self.disabled then return end
+ self:Fire("OnClick")
+ if self.pullout then
+ self.pullout:Close()
+ end
+ end
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ self.frame:SetScript("OnClick", Frame_OnClick)
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
+
+-- Item: Toggle
+-- Some sort of checkbox for dropdown menus.
+-- Does not close the pullout on click.
+do
+ local widgetType = "Dropdown-Item-Toggle"
+ local widgetVersion = 2
+
+ local function UpdateToggle(self)
+ if self.value then
+ self.check:Show()
+ else
+ self.check:Hide()
+ end
+ end
+
+ local function OnRelease(self)
+ ItemBase.OnRelease(self)
+ self:SetValue(nil)
+ end
+
+ local function Frame_OnClick(this, button)
+ local self = this.obj
+ if self.disabled then return end
+ self.value = not self.value
+ UpdateToggle(self)
+ self:Fire("OnValueChanged", self.value)
+ end
+
+ -- exported
+ local function SetValue(self, value)
+ self.value = value
+ UpdateToggle(self)
+ end
+
+ -- exported
+ local function GetValue(self)
+ return self.value
+ end
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ self.frame:SetScript("OnClick", Frame_OnClick)
+
+ self.SetValue = SetValue
+ self.GetValue = GetValue
+ self.OnRelease = OnRelease
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
+
+-- Item: Menu
+-- Shows a submenu on mouse over
+-- Does not close the pullout on click
+do
+ local widgetType = "Dropdown-Item-Menu"
+ local widgetVersion = 2
+
+ local function OnEnter(this)
+ local self = this.obj
+ self:Fire("OnEnter")
+
+ if self.specialOnEnter then
+ self.specialOnEnter(self)
+ end
+
+ self.highlight:Show()
+
+ if not self.disabled and self.submenu then
+ self.submenu:Open("TOPLEFT", self.frame, "TOPRIGHT", self.pullout:GetRightBorderWidth(), 0, self.frame:GetFrameLevel() + 100)
+ end
+ end
+
+ local function OnHide(this)
+ local self = this.obj
+ if self.submenu then
+ self.submenu:Close()
+ end
+ end
+
+ -- exported
+ local function SetMenu(self, menu)
+ assert(menu.type == "Dropdown-Pullout")
+ self.submenu = menu
+ end
+
+ -- exported
+ local function CloseMenu(self)
+ self.submenu:Close()
+ end
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ self.sub:Show()
+
+ self.frame:SetScript("OnEnter", OnEnter)
+ self.frame:SetScript("OnHide", OnHide)
+
+ self.SetMenu = SetMenu
+ self.CloseMenu = CloseMenu
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
+
+-- Item: Separator
+-- A single line to separate items
+do
+ local widgetType = "Dropdown-Item-Separator"
+ local widgetVersion = 1
+
+ -- exported, override
+ local function SetDisabled(self, disabled)
+ ItemBase.SetDisabled(self, disabled)
+ self.useHighlight = false
+ end
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ self.SetDisabled = SetDisabled
+
+ local line = self.frame:CreateTexture(nil, "OVERLAY")
+ line:SetHeight(1)
+ line:SetTexture(.5, .5, .5)
+ line:SetPoint("LEFT", self.frame, "LEFT", 10, 0)
+ line:SetPoint("RIGHT", self.frame, "RIGHT", -10, 0)
+
+ self.text:Hide()
+
+ self.useHighlight = false
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua
new file mode 100644
index 0000000..8713460
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua
@@ -0,0 +1,705 @@
+--[[ $Id: AceGUIWidget-DropDown.lua 877 2009-11-02 15:56:50Z nevcairiel $ ]]--
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local min, max, floor = math.min, math.max, math.floor
+local select, pairs, ipairs = select, pairs, ipairs
+local tsort = table.sort
+
+-- WoW APIs
+local UIParent, CreateFrame = UIParent, CreateFrame
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: CLOSE
+
+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 function fixstrata(strata, parent, ...)
+ local i = 1
+ local child = select(i, ...)
+ parent:SetFrameStrata(strata)
+ while child do
+ fixstrata(strata, child, child:GetChildren())
+ i = i + 1
+ child = select(i, ...)
+ end
+end
+
+do
+ local widgetType = "Dropdown-Pullout"
+ local widgetVersion = 3
+
+ --[[ Static data ]]--
+
+ local backdrop = {
+ bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
+ edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
+ edgeSize = 32,
+ tileSize = 32,
+ tile = true,
+ insets = { left = 11, right = 12, top = 12, bottom = 11 },
+ }
+ 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 = 3, bottom = 3 }
+ }
+
+ local defaultWidth = 200
+ local defaultMaxHeight = 600
+
+ --[[ UI Event Handlers ]]--
+
+ -- HACK: This should be no part of the pullout, but there
+ -- is no other 'clean' way to response to any item-OnEnter
+ -- Used to close Submenus when an other item is entered
+ local function OnEnter(item)
+ local self = item.pullout
+ for k, v in ipairs(self.items) do
+ if v.CloseMenu and v ~= item then
+ v:CloseMenu()
+ end
+ end
+ end
+
+ -- See the note in Constructor() for each scroll related function
+ local function OnMouseWheel(this, value)
+ this.obj:MoveScroll(value)
+ end
+
+ local function OnScrollValueChanged(this, value)
+ this.obj:SetScroll(value)
+ end
+
+ local function OnSizeChanged(this)
+ this.obj:FixScroll()
+ end
+
+ --[[ Exported methods ]]--
+
+ -- exported
+ local function SetScroll(self, value)
+ local status = self.scrollStatus
+ local frame, child = self.scrollFrame, self.itemFrame
+ local height, viewheight = frame:GetHeight(), child:GetHeight()
+
+ local offset
+ if height > viewheight then
+ offset = 0
+ else
+ offset = floor((viewheight - height) / 1000 * value)
+ end
+ child:ClearAllPoints()
+ child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset)
+ child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", self.slider:IsShown() and -12 or 0, offset)
+ status.offset = offset
+ status.scrollvalue = value
+ end
+
+ -- exported
+ local function MoveScroll(self, value)
+ local status = self.scrollStatus
+ local frame, child = self.scrollFrame, self.itemFrame
+ local height, viewheight = frame:GetHeight(), child:GetHeight()
+
+ if height > viewheight then
+ self.slider:Hide()
+ else
+ self.slider:Show()
+ local diff = height - viewheight
+ local delta = 1
+ if value < 0 then
+ delta = -1
+ end
+ self.slider:SetValue(min(max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000))
+ end
+ end
+
+ -- exported
+ local function FixScroll(self)
+ local status = self.scrollStatus
+ local frame, child = self.scrollFrame, self.itemFrame
+ local height, viewheight = frame:GetHeight(), child:GetHeight()
+ local offset = status.offset or 0
+
+ if viewheight < height then
+ self.slider:Hide()
+ child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, offset)
+ self.slider:SetValue(0)
+ else
+ self.slider:Show()
+ local value = (offset / (viewheight - height) * 1000)
+ if value > 1000 then value = 1000 end
+ self.slider:SetValue(value)
+ self:SetScroll(value)
+ if value < 1000 then
+ child:ClearAllPoints()
+ child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset)
+ child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -12, offset)
+ status.offset = offset
+ end
+ end
+ end
+
+ -- exported, AceGUI callback
+ local function OnAcquire(self)
+ self.frame:SetParent(UIParent)
+ --self.itemFrame:SetToplevel(true)
+ end
+
+ -- exported, AceGUI callback
+ local function OnRelease(self)
+ self:Clear()
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ end
+
+ -- exported
+ local function AddItem(self, item)
+ self.items[#self.items + 1] = item
+
+ local h = #self.items * 16
+ self.itemFrame:SetHeight(h)
+ self.frame:SetHeight(min(h + 34, self.maxHeight)) -- +34: 20 for scrollFrame placement (10 offset) and +14 for item placement
+
+ item.frame:SetPoint("LEFT", self.itemFrame, "LEFT")
+ item.frame:SetPoint("RIGHT", self.itemFrame, "RIGHT")
+
+ item:SetPullout(self)
+ item:SetOnEnter(OnEnter)
+ end
+
+ -- exported
+ local function Open(self, point, relFrame, relPoint, x, y)
+ local items = self.items
+ local frame = self.frame
+ local itemFrame = self.itemFrame
+
+ frame:SetPoint(point, relFrame, relPoint, x, y)
+
+
+ local height = 8
+ for i, item in pairs(items) do
+ if i == 1 then
+ item:SetPoint("TOP", itemFrame, "TOP", 0, -2)
+ else
+ item:SetPoint("TOP", items[i-1].frame, "BOTTOM", 0, 1)
+ end
+
+ item:Show()
+
+ height = height + 16
+ end
+ itemFrame:SetHeight(height)
+ fixstrata("TOOLTIP", frame, frame:GetChildren())
+ frame:Show()
+ self:Fire("OnOpen")
+ end
+
+ -- exported
+ local function Close(self)
+ self.frame:Hide()
+ self:Fire("OnClose")
+ end
+
+ -- exported
+ local function Clear(self)
+ local items = self.items
+ for i, item in pairs(items) do
+ AceGUI:Release(item)
+ items[i] = nil
+ end
+ end
+
+ -- exported
+ local function IterateItems(self)
+ return ipairs(self.items)
+ end
+
+ -- exported
+ local function SetHideOnLeave(self, val)
+ self.hideOnLeave = val
+ end
+
+ -- exported
+ local function SetMaxHeight(self, height)
+ self.maxHeight = height or defaultMaxHeight
+ if self.frame:GetHeight() > height then
+ self.frame:SetHeight(height)
+ elseif (self.itemFrame:GetHeight() + 34) < height then
+ self.frame:SetHeight(self.itemFrame:GetHeight() + 34) -- see :AddItem
+ end
+ end
+
+ -- exported
+ local function GetRightBorderWidth(self)
+ return 6 + (self.slider:IsShown() and 12 or 0)
+ end
+
+ -- exported
+ local function GetLeftBorderWidth(self)
+ return 6
+ end
+
+ --[[ Constructor ]]--
+
+ local function Constructor()
+ local count = AceGUI:GetNextWidgetNum(widgetType)
+ local frame = CreateFrame("Frame", "AceGUI30Pullout"..count, UIParent)
+ local self = {}
+ self.count = count
+ self.type = widgetType
+ self.frame = frame
+ frame.obj = self
+
+ self.OnAcquire = OnAcquire
+ self.OnRelease = OnRelease
+
+ self.AddItem = AddItem
+ self.Open = Open
+ self.Close = Close
+ self.Clear = Clear
+ self.IterateItems = IterateItems
+ self.SetHideOnLeave = SetHideOnLeave
+
+ self.SetScroll = SetScroll
+ self.MoveScroll = MoveScroll
+ self.FixScroll = FixScroll
+
+ self.SetMaxHeight = SetMaxHeight
+ self.GetRightBorderWidth = GetRightBorderWidth
+ self.GetLeftBorderWidth = GetLeftBorderWidth
+
+ self.items = {}
+
+ self.scrollStatus = {
+ scrollvalue = 0,
+ }
+
+ self.maxHeight = defaultMaxHeight
+
+ frame:SetBackdrop(backdrop)
+ frame:SetBackdropColor(0, 0, 0)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+ frame:SetClampedToScreen(true)
+ frame:SetWidth(defaultWidth)
+ frame:SetHeight(self.maxHeight)
+ --frame:SetToplevel(true)
+
+ -- NOTE: The whole scroll frame code is copied from the AceGUI-3.0 widget ScrollFrame
+ local scrollFrame = CreateFrame("ScrollFrame", nil, frame)
+ local itemFrame = CreateFrame("Frame", nil, scrollFrame)
+
+ self.scrollFrame = scrollFrame
+ self.itemFrame = itemFrame
+
+ scrollFrame.obj = self
+ itemFrame.obj = self
+
+ local slider = CreateFrame("Slider", "AceGUI30PulloutScrollbar"..count, scrollFrame)
+ slider:SetOrientation("VERTICAL")
+ slider:SetHitRectInsets(0, 0, -10, 0)
+ slider:SetBackdrop(sliderBackdrop)
+ slider:SetWidth(8)
+ slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical")
+ slider:SetFrameStrata("FULLSCREEN_DIALOG")
+ self.slider = slider
+ slider.obj = self
+
+ scrollFrame:SetScrollChild(itemFrame)
+ scrollFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 6, -12)
+ scrollFrame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -6, 12)
+ scrollFrame:EnableMouseWheel(true)
+ scrollFrame:SetScript("OnMouseWheel", OnMouseWheel)
+ scrollFrame:SetScript("OnSizeChanged", OnSizeChanged)
+ scrollFrame:SetToplevel(true)
+ scrollFrame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ itemFrame:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 0, 0)
+ itemFrame:SetPoint("TOPRIGHT", scrollFrame, "TOPRIGHT", -12, 0)
+ itemFrame:SetHeight(400)
+ itemFrame:SetToplevel(true)
+ itemFrame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ slider:SetPoint("TOPLEFT", scrollFrame, "TOPRIGHT", -16, 0)
+ slider:SetPoint("BOTTOMLEFT", scrollFrame, "BOTTOMRIGHT", -16, 0)
+ slider:SetScript("OnValueChanged", OnScrollValueChanged)
+ slider:SetMinMaxValues(0, 1000)
+ slider:SetValueStep(1)
+ slider:SetValue(0)
+
+ scrollFrame:Show()
+ itemFrame:Show()
+ slider:Hide()
+
+ self:FixScroll()
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+end
+
+do
+ local widgetType = "Dropdown"
+ local widgetVersion = 21
+
+ --[[ Static data ]]--
+
+ --[[ UI event handler ]]--
+
+ local function Control_OnEnter(this)
+ this.obj:Fire("OnEnter")
+ end
+
+ local function Control_OnLeave(this)
+ this.obj:Fire("OnLeave")
+ end
+
+ local function Dropdown_OnHide(this)
+ local self = this.obj
+ if self.open then
+ self.pullout:Close()
+ end
+ end
+
+ local function Dropdown_TogglePullout(this)
+ local self = this.obj
+ if self.open then
+ self.open = nil
+ self.pullout:Close()
+ AceGUI:ClearFocus()
+ else
+ self.open = true
+ self.pullout:SetWidth(self.frame:GetWidth())
+ self.pullout:Open("TOPLEFT", self.frame, "BOTTOMLEFT", 0, self.label:IsShown() and -2 or 0)
+ AceGUI:SetFocus(self)
+ end
+ end
+
+ local function OnPulloutOpen(this)
+ local self = this.userdata.obj
+ local value = self.value
+
+ if not self.multiselect then
+ for i, item in this:IterateItems() do
+ item:SetValue(item.userdata.value == value)
+ end
+ end
+
+ self.open = true
+ end
+
+ local function OnPulloutClose(this)
+ local self = this.userdata.obj
+ self.open = nil
+ self:Fire("OnClosed")
+ end
+
+ local function ShowMultiText(self)
+ local text
+ for i, widget in self.pullout:IterateItems() do
+ if widget.type == "Dropdown-Item-Toggle" then
+ if widget:GetValue() then
+ if text then
+ text = text..", "..widget:GetText()
+ else
+ text = widget:GetText()
+ end
+ end
+ end
+ end
+ self:SetText(text)
+ end
+
+ local function OnItemValueChanged(this, event, checked)
+ local self = this.userdata.obj
+
+ if self.multiselect then
+ self:Fire("OnValueChanged", this.userdata.value, checked)
+ ShowMultiText(self)
+ else
+ if checked then
+ self:SetValue(this.userdata.value)
+ self:Fire("OnValueChanged", this.userdata.value)
+ else
+ this:SetValue(true)
+ end
+ if self.open then
+ self.pullout:Close()
+ end
+ end
+ end
+
+ --[[ Exported methods ]]--
+
+ -- exported, AceGUI callback
+ local function OnAcquire(self)
+ local pullout = AceGUI:Create("Dropdown-Pullout")
+ self.pullout = pullout
+ pullout.userdata.obj = self
+ pullout:SetCallback("OnClose", OnPulloutClose)
+ pullout:SetCallback("OnOpen", OnPulloutOpen)
+ self.pullout.frame:SetFrameLevel(self.frame:GetFrameLevel() + 1)
+ fixlevels(self.pullout.frame, self.pullout.frame:GetChildren())
+
+ self:SetHeight(44)
+ self:SetWidth(200)
+ end
+
+ -- exported, AceGUI callback
+ local function OnRelease(self)
+ if self.open then
+ self.pullout:Close()
+ end
+ AceGUI:Release(self.pullout)
+ self.pullout = nil
+
+ self:SetText("")
+ self:SetLabel("")
+ self:SetDisabled(false)
+ self:SetMultiselect(false)
+
+ self.value = nil
+ self.list = nil
+ self.open = nil
+ self.hasClose = nil
+
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ end
+
+ -- exported
+ 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
+
+ -- exported
+ local function ClearFocus(self)
+ if self.open then
+ self.pullout:Close()
+ end
+ end
+
+ -- exported
+ local function SetText(self, text)
+ self.text:SetText(text or "")
+ end
+
+ -- exported
+ 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
+
+ -- exported
+ local function SetValue(self, value)
+ if self.list then
+ self:SetText(self.list[value] or "")
+ end
+ self.value = value
+ end
+
+ -- exported
+ local function GetValue(self)
+ return self.value
+ end
+
+ -- exported
+ local function SetItemValue(self, item, value)
+ if not self.multiselect then return end
+ for i, widget in self.pullout:IterateItems() do
+ if widget.userdata.value == item then
+ if widget.SetValue then
+ widget:SetValue(value)
+ end
+ end
+ end
+ ShowMultiText(self)
+ end
+
+ -- exported
+ local function SetItemDisabled(self, item, disabled)
+ for i, widget in self.pullout:IterateItems() do
+ if widget.userdata.value == item then
+ widget:SetDisabled(disabled)
+ end
+ end
+ end
+
+ local function AddListItem(self, value, text)
+ local item = AceGUI:Create("Dropdown-Item-Toggle")
+ item:SetText(text)
+ item.userdata.obj = self
+ item.userdata.value = value
+ item:SetCallback("OnValueChanged", OnItemValueChanged)
+ self.pullout:AddItem(item)
+ end
+
+ local function AddCloseButton(self)
+ if not self.hasClose then
+ local close = AceGUI:Create("Dropdown-Item-Execute")
+ close:SetText(CLOSE)
+ self.pullout:AddItem(close)
+ self.hasClose = true
+ end
+ end
+
+ -- exported
+ local sortlist = {}
+ local function SetList(self, list)
+ self.list = list
+ self.pullout:Clear()
+ self.hasClose = nil
+ if not list then return end
+
+ for v in pairs(list) do
+ sortlist[#sortlist + 1] = v
+ end
+ tsort(sortlist)
+
+ for i, value in pairs(sortlist) do
+ AddListItem(self, value, list[value])
+ sortlist[i] = nil
+ end
+ if self.multiselect then
+ ShowMultiText(self)
+ AddCloseButton(self)
+ end
+ end
+
+ -- exported
+ local function AddItem(self, value, text)
+ if self.list then
+ self.list[value] = text
+ AddListItem(self, value, text)
+ end
+ end
+
+ -- exported
+ local function SetMultiselect(self, multi)
+ self.multiselect = multi
+ if multi then
+ ShowMultiText(self)
+ AddCloseButton(self)
+ end
+ end
+
+ -- exported
+ local function GetMultiselect(self)
+ return self.multiselect
+ end
+
+ --[[ Constructor ]]--
+
+ local function Constructor()
+ local count = AceGUI:GetNextWidgetNum(widgetType)
+ local frame = CreateFrame("Frame", nil, UIParent)
+ local dropdown = CreateFrame("Frame", "AceGUI30DropDown"..count, frame, "UIDropDownMenuTemplate")
+
+ local self = {}
+ self.type = widgetType
+ self.frame = frame
+ self.dropdown = dropdown
+ self.count = count
+ frame.obj = self
+ dropdown.obj = self
+
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+
+ self.ClearFocus = ClearFocus
+
+ self.SetText = SetText
+ self.SetValue = SetValue
+ self.GetValue = GetValue
+ self.SetList = SetList
+ self.SetLabel = SetLabel
+ self.SetDisabled = SetDisabled
+ self.AddItem = AddItem
+ self.SetMultiselect = SetMultiselect
+ self.GetMultiselect = GetMultiselect
+ self.SetItemValue = SetItemValue
+ self.SetItemDisabled = SetItemDisabled
+
+ self.alignoffset = 31
+
+ 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)
+
+ 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 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
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+end
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDownGroup.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDownGroup.lua
new file mode 100644
index 0000000..cebfc7a
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDownGroup.lua
@@ -0,0 +1,178 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local assert, pairs, type = assert, pairs, type
+
+-- WoW APIs
+local CreateFrame = CreateFrame
+
+--[[
+ 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 = 13
+
+ local function OnAcquire(self)
+ self.dropdown:SetText("")
+ self:SetDropdownWidth(200)
+ self:SetTitle("")
+ end
+
+ local function OnRelease(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)
+ self.dropdown.frame:ClearAllPoints()
+ if title and title ~= "" then
+ self.dropdown.frame:SetPoint("TOPRIGHT", self.frame, "TOPRIGHT", -2, 0)
+ else
+ self.dropdown.frame:SetPoint("TOPLEFT", self.frame, "TOPLEFT", -1, 0)
+ end
+ end
+
+
+ local function SelectedGroup(self,event,value)
+ local group = self.parentgroup
+ local status = group.status or group.localstatus
+ status.selected = value
+ self.parentgroup:Fire("OnGroupSelected", value)
+ end
+
+ local function SetGroupList(self,list)
+ self.dropdown:SetList(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.selected = group
+ self:Fire("OnGroupSelected", group)
+ end
+
+ local function OnWidthSet(self, width)
+ local content = self.content
+ local contentwidth = width - 26
+ 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 - 63
+ if contentheight < 0 then
+ contentheight = 0
+ end
+ content:SetHeight(contentheight)
+ content.height = contentheight
+ end
+
+ local function LayoutFinished(self, width, height)
+ self:SetHeight((height or 0) + 63)
+ end
+
+ local function SetDropdownWidth(self, width)
+ self.dropdown:SetWidth(width)
+ end
+
+ local function Constructor()
+ local frame = CreateFrame("Frame")
+ local self = {}
+ self.type = Type
+
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+
+ self.SetTitle = SetTitle
+ self.SetGroupList = SetGroupList
+ self.SetGroup = SetGroup
+ self.SetStatusTable = SetStatusTable
+ self.SetDropdownWidth = SetDropdownWidth
+ self.OnWidthSet = OnWidthSet
+ self.OnHeightSet = OnHeightSet
+ self.LayoutFinished = LayoutFinished
+
+ 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", 4, -5)
+ titletext:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -4, -5)
+ titletext:SetJustifyH("LEFT")
+ titletext:SetHeight(18)
+ self.titletext = titletext
+
+ local dropdown = AceGUI:Create("Dropdown")
+ self.dropdown = dropdown
+ dropdown.frame:SetParent(frame)
+ dropdown.frame:SetFrameLevel(dropdown.frame:GetFrameLevel() + 2)
+ dropdown.parentgroup = self
+ dropdown:SetCallback("OnValueChanged",SelectedGroup)
+ dropdown.frame:SetPoint("TOPLEFT",frame,"TOPLEFT", -1, 0)
+ dropdown.frame:Show()
+ dropdown:SetLabel("")
+
+ local border = CreateFrame("Frame",nil,frame)
+ self.border = border
+ border:SetPoint("TOPLEFT",frame,"TOPLEFT",0,-26)
+ border:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,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
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-EditBox.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-EditBox.lua
new file mode 100644
index 0000000..ab7578b
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-EditBox.lua
@@ -0,0 +1,233 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local tostring = tostring
+
+-- WoW APIs
+local GetCursorInfo, ClearCursor, GetSpellName = GetCursorInfo, ClearCursor, GetSpellName
+local CreateFrame, UIParent = CreateFrame, UIParent
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: AceGUIEditBoxInsertLink, ChatFontNormal, OKAY
+
+local Type = "EditBox"
+local Version = 13
+
+if not AceGUIEditBoxInsertLink then
+ -- upgradeable hook
+ hooksecurefunc("ChatEdit_InsertLink", function(...) return _G.AceGUIEditBoxInsertLink(...) end)
+end
+
+function _G.AceGUIEditBoxInsertLink(text)
+ for i = 1, AceGUI:GetNextWidgetNum(Type)-1 do
+ local editbox = _G["AceGUI-3.0EditBox"..i]
+ if editbox and editbox:IsVisible() and editbox:HasFocus() then
+ editbox:Insert(text)
+ return true
+ end
+ end
+end
+
+
+--------------------------
+-- Edit box --
+--------------------------
+--[[
+ Events :
+ OnTextChanged
+ OnEnterPressed
+
+]]
+do
+ local function OnAcquire(self)
+ self:SetHeight(26)
+ self:SetWidth(200)
+ self:SetDisabled(false)
+ self:SetLabel()
+ self.showbutton = true
+ end
+
+ local function OnRelease(self)
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ self:SetDisabled(false)
+ self:SetText()
+ 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)
+ AceGUI:ClearFocus()
+ end
+
+ local function EditBox_OnTextChanged(this)
+ local self = this.obj
+ local value = this:GetText()
+ if tostring(value) ~= tostring(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 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:SetHeight(44)
+ self.alignoffset = 30
+ else
+ self.label:SetText("")
+ self.label:Hide()
+ self.editbox:SetPoint("TOPLEFT",self.frame,"TOPLEFT",7,0)
+ self:SetHeight(26)
+ self.alignoffset = 12
+ end
+ end
+
+
+ local function Constructor()
+ local num = AceGUI:GetNextWidgetNum(Type)
+ local frame = CreateFrame("Frame",nil,UIParent)
+ local editbox = CreateFrame("EditBox","AceGUI-3.0EditBox"..num,frame,"InputBoxTemplate")
+
+ local self = {}
+ self.type = Type
+ self.num = num
+
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+
+ self.SetDisabled = SetDisabled
+ self.SetText = SetText
+ 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(40)
+ button:SetHeight(20)
+ button:SetPoint("RIGHT",editbox,"RIGHT",-2,0)
+ button:SetText(OKAY)
+ 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
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Frame.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Frame.lua
new file mode 100644
index 0000000..5a63034
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Frame.lua
@@ -0,0 +1,305 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local pairs, assert, type = pairs, assert, type
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: CLOSE
+
+----------------
+-- Main Frame --
+----------------
+--[[
+ Events :
+ OnClose
+
+]]
+do
+ local Type = "Frame"
+ local Version = 9
+
+ 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 closeOnClick(this)
+ this.obj:Hide()
+ end
+
+ local function frameOnMouseDown(this)
+ AceGUI:ClearFocus()
+ end
+
+ local function titleOnMouseDown(this)
+ this:GetParent():StartMoving()
+ AceGUI:ClearFocus()
+ 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")
+ AceGUI:ClearFocus()
+ end
+
+ local function sizersOnMouseDown(this)
+ this:GetParent():StartSizing("BOTTOM")
+ AceGUI:ClearFocus()
+ end
+
+ local function sizereOnMouseDown(this)
+ this:GetParent():StartSizing("RIGHT")
+ AceGUI:ClearFocus()
+ 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 OnAcquire(self)
+ self.frame:SetParent(UIParent)
+ self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
+ self:ApplyStatus()
+ end
+
+ local function OnRelease(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
+ self:SetWidth(status.width or 700)
+ self: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 - 34
+ 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.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+ 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:SetScript("OnMouseDown", frameOnMouseDown)
+
+ frame:SetBackdrop(FrameBackdrop)
+ frame:SetBackdropColor(0,0,0,1)
+ frame:SetScript("OnHide",frameOnClose)
+ frame:SetMinResize(400,200)
+ 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",titleOnMouseDown)
+ 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", frameOnMouseUp)
+ 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", frameOnMouseUp)
+ 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", frameOnMouseUp)
+ 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
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Heading.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Heading.lua
new file mode 100644
index 0000000..a39817e
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Heading.lua
@@ -0,0 +1,76 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+--------------------------
+-- Heading --
+--------------------------
+do
+ local Type = "Heading"
+ local Version = 5
+
+ local function OnAcquire(self)
+ self:SetText("")
+ self:SetFullWidth()
+ self:SetHeight(18)
+ end
+
+ local function OnRelease(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.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+ 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
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Icon.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Icon.lua
new file mode 100644
index 0000000..3c4e043
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Icon.lua
@@ -0,0 +1,149 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local select = select
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+--------------------------
+-- Label --
+--------------------------
+do
+ local Type = "Icon"
+ local Version = 11
+
+ local function OnAcquire(self)
+ self:SetHeight(110)
+ self:SetWidth(110)
+ self:SetLabel("")
+ self:SetImage(nil)
+ self:SetImageSize(64, 64)
+ end
+
+ local function OnRelease(self)
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ self:SetDisabled(false)
+ end
+
+ local function SetLabel(self, text)
+ if text and text ~= "" then
+ self.label:Show()
+ self.label:SetText(text)
+ self.frame:SetHeight(self.image:GetHeight() + 25)
+ else
+ self.label:Hide()
+ self.frame:SetHeight(self.image:GetHeight() + 10)
+ end
+ 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(...)
+ else
+ image:SetTexCoord(0, 1, 0, 1)
+ end
+ else
+ self.imageshown = nil
+ end
+ end
+
+ local function SetImageSize(self, width, height)
+ self.image:SetWidth(width)
+ self.image:SetHeight(height)
+ --self.frame:SetWidth(width + 30)
+ if self.label:IsShown() then
+ self.frame:SetHeight(height + 25)
+ else
+ self.frame:SetHeight(height + 10)
+ end
+ end
+
+ local function SetDisabled(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.frame:Disable()
+ self.label:SetTextColor(0.5,0.5,0.5)
+ self.image:SetVertexColor(0.5, 0.5, 0.5, 0.5)
+ else
+ self.frame:Enable()
+ self.label:SetTextColor(1,1,1)
+ self.image:SetVertexColor(1, 1, 1)
+ end
+ end
+
+ local function OnClick(this, button)
+ this.obj:Fire("OnClick", button)
+ AceGUI:ClearFocus()
+ end
+
+ local function OnEnter(this)
+ this.obj.highlight:Show()
+ this.obj:Fire("OnEnter")
+ end
+
+ local function OnLeave(this)
+ this.obj.highlight:Hide()
+ this.obj:Fire("OnLeave")
+ end
+
+ local function Constructor()
+ local frame = CreateFrame("Button",nil,UIParent)
+ local self = {}
+ self.type = Type
+
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+ self.SetLabel = SetLabel
+ self.frame = frame
+ self.SetImage = SetImage
+ self.SetImageSize = SetImageSize
+
+ -- SetText should be deprecated along the way
+ self.SetText = SetLabel
+ self.SetDisabled = SetDisabled
+
+ 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,0)
+ label:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0)
+ 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,-5)
+
+ 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
+
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-InlineGroup.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-InlineGroup.lua
new file mode 100644
index 0000000..68742c4
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-InlineGroup.lua
@@ -0,0 +1,138 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-------------
+-- 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 = 6
+
+ local function OnAcquire(self)
+ self:SetWidth(300)
+ self:SetHeight(100)
+ end
+
+ local function OnRelease(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)
+ if self.noAutoHeight then return end
+ 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.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+ 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",0,-17)
+ border:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-1,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
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-InteractiveLabel.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-InteractiveLabel.lua
new file mode 100644
index 0000000..1e8dde3
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-InteractiveLabel.lua
@@ -0,0 +1,219 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local select, max = select, math.max
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GameFontHighlightSmall
+
+--------------------------
+-- Label --
+--------------------------
+do
+ local Type = "InteractiveLabel"
+ local Version = 6
+
+ local function OnAcquire(self)
+ self:SetHeight(18)
+ self:SetWidth(200)
+ self:SetText("")
+ self:SetImage(nil)
+ self:SetColor()
+ self:SetFontObject()
+ self:SetHighlight()
+ self:SetHighlightTexCoord()
+ end
+
+ local function OnRelease(self)
+ self:SetDisabled(false)
+ 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
+ local imageheight = image:GetHeight()
+ local labelheight = label:GetHeight()
+ --center image with label
+ if imageheight > labelheight then
+ image:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0)
+ label:SetPoint("LEFT",image,"RIGHT",0,0)
+ else
+ label:SetPoint("TOPLEFT",frame,"TOPLEFT",imagewidth,0)
+ image:SetPoint("RIGHT",label,"LEFT",0,0)
+ end
+ label:SetWidth(width - imagewidth)
+ height = max(imageheight, labelheight)
+ end
+ else
+ --no image shown
+ label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0)
+ label:SetWidth(width)
+ height = self.label:GetHeight()
+ end
+
+ self.resizing = true
+ self.frame:SetHeight(height)
+ self.frame.height = height
+ self.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)
+ if self.resizing then return end
+ UpdateImageAnchor(self)
+ 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 SetFont(self, font, height, flags)
+ self.label:SetFont(font, height, flags)
+ end
+
+ local function SetFontObject(self, font)
+ self.label:SetFontObject(font or GameFontHighlightSmall)
+ end
+
+ local function SetImageSize(self, width, height)
+ self.image:SetWidth(width)
+ self.image:SetHeight(height)
+ UpdateImageAnchor(self)
+ end
+
+ local function SetHighlight(self, ...)
+ self.highlight:SetTexture(...)
+ end
+
+ local function SetHighlightTexCoord(self, ...)
+ if select('#', ...) >= 1 then
+ self.highlight:SetTexCoord(...)
+ else
+ self.highlight:SetTexCoord(0, 1, 0, 1)
+ end
+ end
+
+ local function SetDisabled(self,disabled)
+ self.disabled = disabled
+ if disabled then
+ self.frame:EnableMouse(false)
+ self.label:SetTextColor(0.5, 0.5, 0.5)
+ else
+ self.frame:EnableMouse(true)
+ self.label:SetTextColor(1, 1, 1)
+ end
+ end
+
+ local function OnEnter(this)
+ this.obj.highlight:Show()
+ this.obj:Fire("OnEnter")
+ end
+
+ local function OnLeave(this)
+ this.obj.highlight:Hide()
+ this.obj:Fire("OnLeave")
+ end
+
+ local function OnClick(this, ...)
+ this.obj:Fire("OnClick", ...)
+ AceGUI:ClearFocus()
+ end
+
+ local function Constructor()
+ local frame = CreateFrame("Frame",nil,UIParent)
+ local self = {}
+ self.type = Type
+
+ frame:EnableMouse(true)
+ frame:SetScript("OnEnter", OnEnter)
+ frame:SetScript("OnLeave", OnLeave)
+ frame:SetScript("OnMouseDown", OnClick)
+
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+ self.SetText = SetText
+ self.SetColor = SetColor
+ self.frame = frame
+ self.OnWidthSet = OnWidthSet
+ self.SetImage = SetImage
+ self.SetImageSize = SetImageSize
+ self.SetFont = SetFont
+ self.SetFontObject = SetFontObject
+ self.SetHighlight = SetHighlight
+ self.SetHighlightTexCoord = SetHighlightTexCoord
+ self.SetDisabled = SetDisabled
+ frame.obj = self
+
+ frame:SetHeight(18)
+ frame:SetWidth(200)
+ 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 highlight = frame:CreateTexture(nil, "OVERLAY")
+ highlight:SetTexture(nil)
+ highlight:SetAllPoints()
+ highlight:SetBlendMode("ADD")
+ highlight:Hide()
+ self.highlight = highlight
+
+ local image = frame:CreateTexture(nil,"BACKGROUND")
+ self.image = image
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(Type,Constructor,Version)
+end
+
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Keybinding.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Keybinding.lua
new file mode 100644
index 0000000..7d6b13a
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Keybinding.lua
@@ -0,0 +1,230 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+
+-- WoW APIs
+local IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown = IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: NOT_BOUND
+
+--------------------------
+-- Keybinding --
+--------------------------
+
+do
+ local Type = "Keybinding"
+ local Version = 13
+
+ 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
+ AceGUI:ClearFocus()
+ 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
+
+ this:EnableKeyboard(false)
+ self.msgframe:Hide()
+ this:UnlockHighlight()
+ self.waitingForKey = nil
+
+ if not self.disabled then
+ self:SetKey(keyPressed)
+ self:Fire("OnKeyChanged",keyPressed)
+ end
+ 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 OnAcquire(self)
+ self:SetWidth(200)
+ self:SetHeight(44)
+ self:SetLabel("")
+ self:SetKey("")
+ end
+
+ local function OnRelease(self)
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ self.waitingForKey = nil
+ self.msgframe:Hide()
+ self:SetDisabled(false)
+ 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)
+ if (key or "") == "" then
+ self.button:SetText(NOT_BOUND)
+ self.button:SetNormalFontObject("GameFontNormal")
+ else
+ self.button:SetText(key)
+ self.button:SetNormalFontObject("GameFontHighlight")
+ end
+ end
+
+ local function SetLabel(self, label)
+ self.label:SetText(label or "")
+ if (label or "") == "" then
+ self.alignoffset = nil
+ self:SetHeight(24)
+ else
+ self.alignoffset = 30
+ self:SetHeight(44)
+ end
+ end
+
+ local function Constructor()
+ local num = AceGUI:GetNextWidgetNum(Type)
+ local frame = CreateFrame("Frame",nil,UIParent)
+
+ local button = CreateFrame("Button","AceGUI-3.0 KeybindingButton"..num,frame,"UIPanelButtonTemplate2")
+
+ local self = {}
+ self.type = Type
+ self.num = num
+
+ 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.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+ 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
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Label.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Label.lua
new file mode 100644
index 0000000..b499ba7
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Label.lua
@@ -0,0 +1,155 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local max, select = math.max, select
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GameFontHighlightSmall
+
+--------------------------
+-- Label --
+--------------------------
+do
+ local Type = "Label"
+ local Version = 11
+
+ local function OnAcquire(self)
+ self:SetHeight(18)
+ self:SetWidth(200)
+ self:SetText("")
+ self:SetImage(nil)
+ self:SetColor()
+ self:SetFontObject()
+ end
+
+ local function OnRelease(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",4,0)
+ label:SetWidth(width - imagewidth)
+ height = 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
+
+ self.resizing = true
+ self.frame:SetHeight(height)
+ self.frame.height = height
+ self.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)
+ if self.resizing then return end
+ UpdateImageAnchor(self)
+ 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 SetFont(self, font, height, flags)
+ self.label:SetFont(font, height, flags)
+ end
+
+ local function SetFontObject(self, font)
+ self.label:SetFontObject(font or GameFontHighlightSmall)
+ 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.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+ self.SetText = SetText
+ self.SetColor = SetColor
+ self.frame = frame
+ self.OnWidthSet = OnWidthSet
+ self.SetImage = SetImage
+ self.SetImageSize = SetImageSize
+ self.SetFont = SetFont
+ self.SetFontObject = SetFontObject
+ frame.obj = self
+
+ frame:SetHeight(18)
+ frame:SetWidth(200)
+ 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
+
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-MultiLineEditBox.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-MultiLineEditBox.lua
new file mode 100644
index 0000000..22833b6
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-MultiLineEditBox.lua
@@ -0,0 +1,309 @@
+
+--[[
+--Multiline Editbox Widget, Originally by bam
+
+--]]
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local format, pairs, tostring = string.format, pairs, tostring
+
+-- WoW APIs
+local GetCursorInfo, ClearCursor, GetSpellName = GetCursorInfo, ClearCursor, GetSpellName
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: ChatFontNormal, ACCEPT
+
+local Version = 11
+---------------------
+-- 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()
+ AceGUI:ClearFocus()
+ end
+
+ function MultiLineEditBox:OnAcquire()
+ self:SetWidth(200)
+ self:SetHeight(116)
+ self:SetNumLines(4)
+ self:SetDisabled(false)
+ self:ShowButton(true)
+ end
+
+ function MultiLineEditBox:OnRelease()
+ 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.scrollframe:EnableMouse(false)
+ self.editbox:ClearFocus()
+ self.editbox:SetTextColor(0.5, 0.5, 0.5)
+ self.label:SetTextColor(0.5,0.5,0.5)
+ self.button:Disable()
+ else
+ self.editbox:EnableMouse(true)
+ self.scrollframe:EnableMouse(true)
+ self.editbox:SetTextColor(1, 1, 1)
+ self.label:SetTextColor(1,.82,0)
+ self.button:Enable()
+ 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:SetNumLines(number)
+ number = number or 4
+ self:SetHeight(60 + (14*number))
+ 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
+ self.scrollframe = scrollframe
+
+ --local scrollchild = CreateFrame("Frame", nil, scrollframe)
+ --scrollframe:SetScrollChild(scrollchild)
+ --scrollchild:SetHeight(2)
+ --scrollchild:SetWidth(2)
+
+ 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 editbox = CreateFrame("EditBox", nil, scrollframe)
+ self.editbox = editbox
+ editbox.obj = self
+ editbox:SetPoint("TOPLEFT")
+ editbox:SetPoint("BOTTOMLEFT")
+ 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)
+ scrollframe:SetScrollChild(editbox)
+
+ local button = CreateFrame("Button",nil,scrollframe,"UIPanelButtonTemplate")
+ button:SetWidth(80)
+ button:SetHeight(20)
+ button:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,2)
+ button:SetText(ACCEPT)
+ button:SetScript("OnClick", Button_OnClick)
+ button:SetFrameLevel(editbox:GetFrameLevel() + 1)
+ 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
+ if not self.disabled then
+ self.button:Enable()
+ end
+ 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
+
+
+
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-ScrollFrame.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-ScrollFrame.lua
new file mode 100644
index 0000000..0a9f3ad
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-ScrollFrame.lua
@@ -0,0 +1,241 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local pairs, assert, type = pairs, assert, type
+local min, max, floor = math.min, math.max, math.floor
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+
+-------------
+-- 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 = 9
+
+ local function OnAcquire(self)
+
+ end
+
+ local function OnRelease(self)
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ self.status = nil
+ -- do SetScroll after niling status, but before clearing localstatus
+ -- so the scroll value isnt populated back into status, but not kept in localstatus either
+ self:SetScroll(0)
+ for k in pairs(self.localstatus) do
+ self.localstatus[k] = nil
+ end
+ self.scrollframe:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",0,0)
+ self.scrollbar:Hide()
+ self.scrollBarShown = nil
+ self.content.height, self.content.width = nil, nil
+ end
+
+ local function SetScroll(self, value)
+ local status = self.status or self.localstatus
+ local viewheight = self.scrollframe:GetHeight()
+ local height = self.content:GetHeight()
+ local offset
+
+ if viewheight > height then
+ offset = 0
+ else
+ offset = floor((height - viewheight) / 1000.0 * value)
+ end
+ self.content:ClearAllPoints()
+ self.content:SetPoint("TOPLEFT", self.scrollframe, "TOPLEFT", 0, offset)
+ self.content:SetPoint("TOPRIGHT", self.scrollframe, "TOPRIGHT", 0, offset)
+ status.offset = offset
+ status.scrollvalue = value
+ end
+
+ local function MoveScroll(self, value)
+ local status = self.status or self.localstatus
+ local height, viewheight = self.scrollframe:GetHeight(), self.content: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(min(max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000))
+ end
+ end
+
+
+ local function FixScroll(self)
+ if self.updateLock then return end
+ self.updateLock = true
+ local status = self.status or self.localstatus
+ local height, viewheight = self.scrollframe:GetHeight(), self.content:GetHeight()
+ local offset = status.offset or 0
+ local curvalue = self.scrollbar:GetValue()
+ if viewheight < height then
+ if self.scrollBarShown then
+ self.scrollBarShown = nil
+ self.scrollbar:Hide()
+ self.scrollbar:SetValue(0)
+ self.scrollframe:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",0,0)
+ self:DoLayout()
+ end
+ else
+ if not self.scrollBarShown then
+ self.scrollBarShown = true
+ self.scrollbar:Show()
+ self.scrollframe:SetPoint("BOTTOMRIGHT", self.frame,"BOTTOMRIGHT",-20,0)
+ self:DoLayout()
+ end
+ local value = (offset / (viewheight - height) * 1000)
+ if value > 1000 then value = 1000 end
+ self.scrollbar:SetValue(value)
+ self:SetScroll(value)
+ if value < 1000 then
+ self.content:ClearAllPoints()
+ self.content:SetPoint("TOPLEFT", self.scrollframe, "TOPLEFT", 0, offset)
+ self.content:SetPoint("TOPRIGHT", self.scrollframe, "TOPRIGHT", 0, offset)
+ status.offset = offset
+ end
+ end
+ self.updateLock = nil
+ 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)
+ end
+
+ local function LayoutFinished(self, width, height)
+ self.content:SetHeight(height or 0 + 20)
+ self.scrollframe:SetScript("OnUpdate", FixScrollOnUpdate)
+ 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 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.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+
+ 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)
+ scrollframe.obj = self
+ scrollframe:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, 0)
+ scrollframe:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0, 0)
+ scrollframe:EnableMouseWheel(true)
+ scrollframe:SetScript("OnMouseWheel", OnMouseWheel)
+ scrollframe:SetScript("OnSizeChanged", OnSizeChanged)
+ self.scrollframe = scrollframe
+
+ local content = CreateFrame("Frame", nil, scrollframe)
+ content.obj = self
+ content:SetPoint("TOPLEFT", scrollframe, "TOPLEFT", 0, 0)
+ content:SetPoint("TOPRIGHT", scrollframe, "TOPRIGHT", 0, 0)
+ content:SetHeight(400)
+ self.content = content
+ scrollframe:SetScrollChild(content)
+
+ local num = AceGUI:GetNextWidgetNum(Type)
+ local name = ("AceConfigDialogScrollFrame%dScrollBar"):format(num)
+ local scrollbar = CreateFrame("Slider", name, scrollframe, "UIPanelScrollBarTemplate")
+ scrollbar.obj = self
+ scrollbar:SetPoint("TOPLEFT", scrollframe, "TOPRIGHT", 4, -16)
+ scrollbar:SetPoint("BOTTOMLEFT", scrollframe, "BOTTOMRIGHT", 4, 16)
+ scrollbar:SetScript("OnValueChanged", OnScrollValueChanged)
+ scrollbar:SetMinMaxValues(0, 1000)
+ scrollbar:SetValueStep(1)
+ scrollbar:SetValue(0)
+ scrollbar:SetWidth(16)
+ scrollbar:Hide()
+ self.scrollbar = scrollbar
+
+ local scrollbg = scrollbar:CreateTexture(nil, "BACKGROUND")
+ scrollbg:SetAllPoints(scrollbar)
+ scrollbg:SetTexture(0, 0, 0, 0.4)
+
+ self.localstatus.scrollvalue = 0
+
+ --self:FixScroll()
+ AceGUI:RegisterAsContainer(self)
+ --AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(Type,Constructor,Version)
+end
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-SimpleGroup.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-SimpleGroup.lua
new file mode 100644
index 0000000..9f9def5
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-SimpleGroup.lua
@@ -0,0 +1,99 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-------------
+-- 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 = 5
+
+ local function OnAcquire(self)
+ self:SetWidth(300)
+ self:SetHeight(100)
+ end
+
+ local function OnRelease(self)
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ end
+
+ local function LayoutFinished(self, width, height)
+ if self.noAutoHeight then return end
+ 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.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+ 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
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Slider.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Slider.lua
new file mode 100644
index 0000000..14f2867
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Slider.lua
@@ -0,0 +1,282 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local min, max, floor = math.min, math.max, math.floor
+local tonumber = tonumber
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GameFontHighlightSmall
+
+--------------------------
+-- Slider --
+--------------------------
+do
+ local Type = "Slider"
+ local Version = 9
+
+ local function OnAcquire(self)
+ self:SetWidth(200)
+ self:SetHeight(44)
+ self:SetDisabled(false)
+ self:SetIsPercent(nil)
+ self:SetSliderValues(0,100,1)
+ self:SetValue(0)
+ end
+
+ local function OnRelease(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)
+ local value = self.value or 0
+ if self.ispercent then
+ self.editbox:SetText(("%s%%"):format(floor(value*1000+0.5)/10))
+ else
+ self.editbox:SetText(floor(value*100+0.5)/100)
+ end
+ end
+
+ local function UpdateLabels(self)
+ local min, max = (self.min or 0), (self.max or 100)
+ if self.ispercent then
+ self.lowtext:SetFormattedText("%s%%",(min * 100))
+ self.hightext:SetFormattedText("%s%%",(max * 100))
+ else
+ self.lowtext:SetText(min)
+ self.hightext:SetText(max)
+ 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 = min(value + (self.step or 1),self.max)
+ else
+ value = 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)
+ UpdateLabels(self)
+ frame:SetValueStep(step or 1)
+ if self.value then
+ frame:SetValue(self.value)
+ end
+ 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 EditBox_OnEnter(this)
+ this:SetBackdropBorderColor(0.5,0.5,0.5,1)
+ end
+
+ local function EditBox_OnLeave(this)
+ this:SetBackdropBorderColor(0.3,0.3,0.3,0.8)
+ end
+
+ local function SetIsPercent(self, value)
+ self.ispercent = value
+ UpdateLabels(self)
+ UpdateText(self)
+ end
+
+ local function FrameOnMouseDown(this)
+ this.obj.slider:EnableMouseWheel(true)
+ AceGUI:ClearFocus()
+ 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 ManualBackdrop = {
+ bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
+ edgeFile = "Interface\\ChatFrame\\ChatFrameBackground",
+ tile = true, edgeSize = 1, tileSize = 5,
+ }
+
+ local function Constructor()
+ local frame = CreateFrame("Frame",nil,UIParent)
+ local self = {}
+ self.type = Type
+
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+
+ 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(70)
+ editbox:SetJustifyH("CENTER")
+ editbox:EnableMouse(true)
+ editbox:SetScript("OnEscapePressed",EditBox_OnEscapePressed)
+ editbox:SetScript("OnEnterPressed",EditBox_OnEnterPressed)
+ editbox:SetScript("OnEnter",EditBox_OnEnter)
+ editbox:SetScript("OnLeave",EditBox_OnLeave)
+ editbox:SetBackdrop(ManualBackdrop)
+ editbox:SetBackdropColor(0,0,0,0.5)
+ editbox:SetBackdropBorderColor(0.3,0.3,0.30,0.80)
+ 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
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-TabGroup.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-TabGroup.lua
new file mode 100644
index 0000000..12de334
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-TabGroup.lua
@@ -0,0 +1,387 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local pairs, ipairs, assert, type = pairs, ipairs, assert, type
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: PanelTemplates_TabResize, PanelTemplates_SetDisabledTabState, PanelTemplates_SelectTab, PanelTemplates_DeselectTab
+
+-------------
+-- 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 = 24
+
+ 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 OnAcquire(self)
+
+ end
+
+ local function OnRelease(self)
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ self.status = nil
+ for k in pairs(self.localstatus) do
+ self.localstatus[k] = nil
+ end
+ self.tablist = nil
+ for _, tab in pairs(self.tabs) do
+ tab:Hide()
+ end
+ self:SetTitle()
+ end
+
+ local function Tab_SetText(self, text)
+ self:_SetText(text)
+ local width = self.obj.frame.width or self.obj.frame:GetWidth() or 0
+ PanelTemplates_TabResize(self, 0, nil, width)
+ end
+
+ local function UpdateTabLook(self)
+ if self.disabled then
+ PanelTemplates_SetDisabledTabState(self)
+ elseif self.selected then
+ PanelTemplates_SelectTab(self)
+ else
+ PanelTemplates_DeselectTab(self)
+ 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 Tab_OnEnter(this)
+ local self = this.obj
+ self:Fire("OnTabEnter", self.tabs[this.id].value, this)
+ end
+
+ local function Tab_OnLeave(this)
+ local self = this.obj
+ self:Fire("OnTabLeave", self.tabs[this.id].value, this)
+ end
+
+ local function Tab_OnShow(this)
+ _G[this:GetName().."HighlightTexture"]:SetWidth(this:GetTextWidth() + 30)
+ end
+
+ local function CreateTab(self, id)
+ local tabname = "AceGUITabGroup"..self.num.."Tab"..id
+ local tab = CreateFrame("Button",tabname,self.border,"OptionsFrameTabButtonTemplate")
+ tab.obj = self
+ tab.id = id
+
+ tab.text = _G[tabname .. "Text"]
+ tab.text:ClearAllPoints()
+ tab.text:SetPoint("LEFT", tab, "LEFT", 14, -3)
+ tab.text:SetPoint("RIGHT", tab, "RIGHT", -12, -3)
+
+ tab:SetScript("OnClick",Tab_OnClick)
+ tab:SetScript("OnEnter",Tab_OnEnter)
+ tab:SetScript("OnLeave",Tab_OnLeave)
+ tab:SetScript("OnShow", Tab_OnShow)
+
+ tab._SetText = tab.SetText
+ tab.SetText = Tab_SetText
+ tab.SetSelected = Tab_SetSelected
+ tab.SetDisabled = Tab_SetDisabled
+
+ return tab
+ end
+
+ local function SetTitle(self, text)
+ self.titletext:SetText(text or "")
+ if text and text ~= "" then
+ self.alignoffset = 25
+ else
+ self.alignoffset = 18
+ end
+ self:BuildTabs()
+ 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 widths = {}
+ local rowwidths = {}
+ local rowends = {}
+ local function BuildTabs(self)
+ local hastitle = (self.titletext:GetText() and self.titletext:GetText() ~= "")
+ local status = self.status or self.localstatus
+ local tablist = self.tablist
+ local tabs = self.tabs
+
+ if not tablist then return end
+
+ local width = self.frame.width or self.frame:GetWidth() or 0
+
+ for i = #widths, 1, -1 do
+ widths[i] = nil
+ end
+ for i = #rowwidths, 1, -1 do
+ rowwidths[i] = nil
+ end
+ for i = #rowends, 1, -1 do
+ rowends[i] = nil
+ end
+
+ --Place Text into tabs and get thier initial width
+ for i, v in ipairs(tablist) do
+ local tab = tabs[i]
+ if not tab then
+ tab = self:CreateTab(i)
+ tabs[i] = tab
+ end
+
+ tab:Show()
+ tab:SetText(v.text)
+ tab:SetDisabled(v.disabled)
+ tab.value = v.value
+
+ widths[i] = tab:GetWidth() - 6 --tabs are anchored 10 pixels from the right side of the previous one to reduce spacing, but add a fixed 4px padding for the text
+ end
+
+ for i = (#tablist)+1, #tabs, 1 do
+ tabs[i]:Hide()
+ end
+
+ --First pass, find the minimum number of rows needed to hold all tabs and the initial tab layout
+ local numtabs = #tablist
+ local numrows = 1
+ local usedwidth = 0
+
+ for i = 1, #tablist do
+ --If this is not the first tab of a row and there isn't room for it
+ if usedwidth ~= 0 and (width - usedwidth - widths[i]) < 0 then
+ rowwidths[numrows] = usedwidth + 10 --first tab in each row takes up an extra 10px
+ rowends[numrows] = i - 1
+ numrows = numrows + 1
+ usedwidth = 0
+ end
+ usedwidth = usedwidth + widths[i]
+ end
+ rowwidths[numrows] = usedwidth + 10 --first tab in each row takes up an extra 10px
+ rowends[numrows] = #tablist
+
+ --Fix for single tabs being left on the last row, move a tab from the row above if applicable
+ if numrows > 1 then
+ --if the last row has only one tab
+ if rowends[numrows-1] == numtabs-1 then
+ --if there are more than 2 tabs in the 2nd last row
+ if (numrows == 2 and rowends[numrows-1] > 2) or (rowends[numrows] - rowends[numrows-1] > 2) then
+ --move 1 tab from the second last row to the last, if there is enough space
+ if (rowwidths[numrows] + widths[numtabs-1]) <= width then
+ rowends[numrows-1] = rowends[numrows-1] - 1
+ rowwidths[numrows] = rowwidths[numrows] + widths[numtabs-1]
+ rowwidths[numrows-1] = rowwidths[numrows-1] - widths[numtabs-1]
+ end
+ end
+ end
+ end
+
+ --anchor the rows as defined and resize tabs to fill thier row
+ local starttab = 1
+ for row, endtab in ipairs(rowends) do
+ local first = true
+ for tabno = starttab, endtab do
+ local tab = tabs[tabno]
+ tab:ClearAllPoints()
+ if first then
+ tab:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 0, -(hastitle and 14 or 7)-(row-1)*20 )
+ first = false
+ else
+ tab:SetPoint("LEFT", tabs[tabno-1], "RIGHT", -10, 0)
+ end
+ end
+
+ -- equal padding for each tab to fill the available width,
+ -- if the used space is above 75% already
+ local padding = 0
+ if not (numrows == 1 and rowwidths[1] < width*0.75) then
+ padding = (width - rowwidths[row]) / (endtab - starttab+1)
+ end
+
+ for i = starttab, endtab do
+ PanelTemplates_TabResize(tabs[i], padding + 4, nil, width)
+ end
+ starttab = endtab + 1
+ end
+
+ self.borderoffset = (hastitle and 17 or 10)+((numrows)*20)
+ self.border:SetPoint("TOPLEFT",self.frame,"TOPLEFT",1,-self.borderoffset)
+ end
+
+ local function BuildTabsOnUpdate(this)
+ BuildTabs(this.obj)
+ this:SetScript("OnUpdate", nil)
+ 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
+ BuildTabs(self)
+ self.frame:SetScript("OnUpdate", BuildTabsOnUpdate)
+ end
+
+
+ local function OnHeightSet(self, height)
+ local content = self.content
+ local contentheight = height - (self.borderoffset + 23)
+ if contentheight < 0 then
+ contentheight = 0
+ end
+ content:SetHeight(contentheight)
+ content.height = contentheight
+ end
+
+ local function LayoutFinished(self, width, height)
+ if self.noAutoHeight then return end
+ self:SetHeight((height or 0) + (self.borderoffset + 23))
+ end
+
+ local function Constructor()
+ local frame = CreateFrame("Frame",nil,UIParent)
+ local self = {}
+ self.type = Type
+
+ self.num = AceGUI:GetNextWidgetNum(Type)
+
+ self.localstatus = {}
+
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+ self.SetTitle = SetTitle
+ self.CreateTab = CreateTab
+ self.SelectTab = SelectTab
+ self.BuildTabs = BuildTabs
+ self.SetStatusTable = SetStatusTable
+ self.SetTabs = SetTabs
+ self.LayoutFinished = LayoutFinished
+ self.frame = frame
+
+ self.OnWidthSet = OnWidthSet
+ self.OnHeightSet = OnHeightSet
+
+ frame.obj = self
+
+ frame:SetHeight(100)
+ frame:SetWidth(100)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ self.alignoffset = 18
+
+ 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)
+ titletext:SetText("")
+
+ self.titletext = titletext
+
+ local border = CreateFrame("Frame",nil,frame)
+ self.border = border
+ self.borderoffset = 27
+ border:SetPoint("TOPLEFT",frame,"TOPLEFT",1,-27)
+ border:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-1,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,-7)
+ content:SetPoint("BOTTOMRIGHT",border,"BOTTOMRIGHT",-10,7)
+
+ AceGUI:RegisterAsContainer(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(Type,Constructor,Version)
+end
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-TreeGroup.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-TreeGroup.lua
new file mode 100644
index 0000000..cd5ecbc
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-TreeGroup.lua
@@ -0,0 +1,746 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local next, pairs, ipairs, assert, type = next, pairs, ipairs, assert, type
+local math_min, math_max, floor = math.min, math.max, floor
+local select, tremove, unpack = select, table.remove, unpack
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GameTooltip, FONT_COLOR_CODE_CLOSE
+
+-- 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
+
+--------------
+-- TreeView --
+--------------
+
+do
+ local Type = "TreeGroup"
+ local Version = 23
+
+ local DEFAULT_TREE_WIDTH = 175
+ local DEFAULT_TREE_SIZABLE = true
+
+ 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 DraggerBackdrop = {
+ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
+ edgeFile = nil,
+ tile = true, tileSize = 16, edgeSize = 0,
+ insets = { left = 3, right = 3, top = 7, bottom = 7 }
+ }
+
+ local function OnAcquire(self)
+ self:SetTreeWidth(DEFAULT_TREE_WIDTH,DEFAULT_TREE_SIZABLE)
+ self:EnableButtonTooltips(true)
+ end
+
+ local function OnRelease(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
+ self.localstatus.treewidth = DEFAULT_TREE_WIDTH
+ self.localstatus.treesizable = DEFAULT_TREE_SIZABLE
+ 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
+ AceGUI:ClearFocus()
+ 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 function EnableButtonTooltips(self, enable)
+ self.enabletooltips = enable
+ end
+
+ local function Button_OnEnter(this)
+ local self = this.obj
+ self:Fire("OnButtonEnter", this.uniquevalue, this)
+
+ if self.enabletooltips then
+ GameTooltip:SetOwner(this, "ANCHOR_NONE")
+ GameTooltip:SetPoint("LEFT",this,"RIGHT")
+ GameTooltip:SetText(this.text:GetText() or "", 1, .82, 0, 1)
+
+ GameTooltip:Show()
+ end
+ end
+
+ local function Button_OnLeave(this)
+ local self = this.obj
+ self:Fire("OnButtonLeave", this.uniquevalue, this)
+
+ if self.enabletooltips then
+ GameTooltip:Hide()
+ end
+ end
+
+
+ local buttoncount = 1
+ local function CreateButton(self)
+ local button = CreateFrame("Button",("AceGUI30TreeButton%d"):format(buttoncount),self.treeframe, "OptionsListButtonTemplate")
+ buttoncount = buttoncount + 1
+ button.obj = self
+
+ local icon = button:CreateTexture(nil, "OVERLAY")
+ icon:SetWidth(14)
+ icon:SetHeight(14)
+ button.icon = icon
+
+ button:SetScript("OnClick",ButtonOnClick)
+ button:SetScript("OnDoubleClick", ButtonOnDoubleClick)
+ button:SetScript("OnEnter",Button_OnEnter)
+ button:SetScript("OnLeave",Button_OnLeave)
+
+ 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 icon = treeline.icon
+ local iconCoords = treeline.iconCoords
+ 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 normalTexture = button:GetNormalTexture()
+ local line = button.line
+ button.level = level
+ if ( level == 1 ) then
+ button:SetNormalFontObject("GameFontNormal")
+ button:SetHighlightFontObject("GameFontHighlight")
+ button.text:SetPoint("LEFT", (icon and 16 or 0) + 8, 2)
+ else
+ button:SetNormalFontObject("GameFontHighlightSmall")
+ button:SetHighlightFontObject("GameFontHighlightSmall")
+ button.text:SetPoint("LEFT", (icon and 16 or 0) + 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 icon then
+ button.icon:SetTexture(icon)
+ button.icon:SetPoint("LEFT", button, "LEFT", 8 * level, (level == 1) and 0 or 1)
+ else
+ button.icon:SetTexture(nil)
+ end
+
+ if iconCoords then
+ button.icon:SetTexCoord(unpack(iconCoords))
+ else
+ button.icon:SetTexCoord(0, 1, 0, 1)
+ 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()
+ AceGUI:ClearFocus()
+ 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
+ if not status.treewidth then
+ status.treewidth = DEFAULT_TREE_WIDTH
+ end
+ if not status.treesizable then
+ status.treesizable = DEFAULT_TREE_SIZABLE
+ end
+ self:SetTreeWidth(status.treewidth,status.treesizable)
+ 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, filter)
+ self.filter = filter
+ if tree then
+ assert(type(tree) == "table")
+ end
+ self.tree = tree
+ self:RefreshTree()
+ end
+
+ local function ShouldDisplayLevel(tree)
+ local result = false
+ for k, v in ipairs(tree) do
+ if v.children == nil and v.visible ~= false then
+ result = true
+ elseif v.children then
+ result = result or ShouldDisplayLevel(v.children)
+ end
+ if result then return result end
+ end
+ return false
+ end
+
+ local function addLine(self, v, tree, level, parent)
+ local line = new()
+ line.value = v.value
+ line.text = v.text
+ line.icon = v.icon
+ line.iconCoords = v.iconCoords
+ line.disabled = v.disabled
+ line.tree = tree
+ line.level = level
+ line.parent = parent
+ line.visible = v.visible
+ line.uniquevalue = GetButtonUniqueValue(line)
+ if v.children then
+ line.hasChildren = true
+ else
+ line.hasChildren = nil
+ end
+ self.lines[#self.lines+1] = line
+ return line
+ end
+
+ local function BuildLevel(self, tree, level, parent)
+ local groups = (self.status or self.localstatus).groups
+ local hasChildren = self.hasChildren
+
+ for i, v in ipairs(tree) do
+ if v.children then
+ if not self.filter or ShouldDisplayLevel(v.children) then
+ local line = addLine(self, v, tree, level, parent)
+ if groups[line.uniquevalue] then
+ self:BuildLevel(v.children, level+1, line)
+ end
+ end
+ elseif v.visible ~= false or not self.filter then
+ addLine(self, v, tree, level, parent)
+ 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)
+ local buttons = self.buttons
+ local lines = self.lines
+
+ for i, v in ipairs(buttons) do
+ v:Hide()
+ end
+ while lines[1] do
+ local t = tremove(lines)
+ for k in pairs(t) do
+ t[k] = nil
+ end
+ del(t)
+ end
+
+ 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 treeframe = self.treeframe
+
+ self:BuildLevel(tree, 1)
+
+ local numlines = #lines
+
+ local maxlines = (floor(((self.treeframe:GetHeight()or 0) - 20 ) / 18))
+
+ 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, ...)
+ self.filter = false
+ 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, ("\001"):split(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 treeframe = self.treeframe
+ local status = self.status or self.localstatus
+ status.fullwidth = width
+
+ local contentwidth = width - status.treewidth - 20
+ if contentwidth < 0 then
+ contentwidth = 0
+ end
+ content:SetWidth(contentwidth)
+ content.width = contentwidth
+
+ local maxtreewidth = math_min(400, width - 50)
+
+ if maxtreewidth > 100 and status.treewidth > maxtreewidth then
+ self:SetTreeWidth(maxtreewidth, status.treesizable)
+ end
+ treeframe:SetMaxResize(maxtreewidth,1600)
+ 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 function SetTreeWidth(self, treewidth, resizable)
+ if not resizable then
+ if type(treewidth) == 'number' then
+ resizable = false
+ elseif type(treewidth) == 'boolean' then
+ resizable = treewidth
+ treewidth = DEFAULT_TREE_WIDTH
+ else
+ resizable = false
+ treewidth = DEFAULT_TREE_WIDTH
+ end
+ end
+ self.treeframe:SetWidth(treewidth)
+ self.dragger:EnableMouse(resizable)
+
+ local status = self.status or self.localstatus
+ status.treewidth = treewidth
+ status.treesizable = resizable
+
+ -- recalculate the content width
+ if status.fullwidth then
+ self:OnWidthSet(status.fullwidth)
+ end
+ end
+
+ local function draggerLeave(this)
+ this:SetBackdropColor(1, 1, 1, 0)
+ end
+
+ local function draggerEnter(this)
+ this:SetBackdropColor(1, 1, 1, 0.8)
+ end
+
+ local function draggerDown(this)
+ local treeframe = this:GetParent()
+ treeframe:StartSizing("RIGHT")
+ end
+
+ local function draggerUp(this)
+ local treeframe = this:GetParent()
+ local self = treeframe.obj
+ local frame = treeframe:GetParent()
+ treeframe:StopMovingOrSizing()
+ --treeframe:SetScript("OnUpdate", nil)
+ treeframe:SetUserPlaced(false)
+ --Without this :GetHeight will get stuck on the current height, causing the tree contents to not resize
+ treeframe:SetHeight(0)
+ treeframe:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0)
+ treeframe:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0)
+
+ local status = self.status or self.localstatus
+ status.treewidth = treeframe:GetWidth()
+
+ treeframe.obj:Fire("OnTreeResize",treeframe:GetWidth())
+ -- recalculate the content width
+ treeframe.obj:OnWidthSet(status.fullwidth)
+ -- update the layout of the content
+ treeframe.obj:DoLayout()
+ end
+
+ local function LayoutFinished(self, width, height)
+ if self.noAutoHeight then return end
+ self:SetHeight((height or 0) + 20)
+ 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 = {}
+ self.filter = false
+
+ 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(DEFAULT_TREE_WIDTH)
+ 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)
+
+ treeframe:SetResizable(true)
+ treeframe:SetMinResize(100, 1)
+ treeframe:SetMaxResize(400,1600)
+ local dragger = CreateFrame("Frame", nil, treeframe)
+ dragger:SetWidth(8)
+ dragger:SetPoint("TOP", treeframe, "TOPRIGHT")
+ dragger:SetPoint("BOTTOM", treeframe, "BOTTOMRIGHT")
+ dragger:SetBackdrop(DraggerBackdrop)
+ dragger:SetBackdropColor(1, 1, 1, 0)
+ dragger:SetScript("OnMouseDown", draggerDown)
+ dragger:SetScript("OnMouseUp", draggerUp)
+ dragger:SetScript("OnEnter", draggerEnter)
+ dragger:SetScript("OnLeave", draggerLeave)
+
+ self.dragger = dragger
+ self.treeframe = treeframe
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+
+ self.SetTree = SetTree
+ self.SetTreeWidth = SetTreeWidth
+ 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.EnableButtonTooltips = EnableButtonTooltips
+ --self.Filter = Filter
+ self.LayoutFinished = LayoutFinished
+
+ 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",treeframe,"TOPRIGHT", 0,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
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Window.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Window.lua
new file mode 100644
index 0000000..3474e3a
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Window.lua
@@ -0,0 +1,328 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local pairs, assert, type = pairs, assert, type
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GameFontNormal
+
+----------------
+-- Main Frame --
+----------------
+--[[
+ Events :
+ OnClose
+
+]]
+do
+ local Type = "Window"
+ local Version = 2
+
+ local function frameOnClose(this)
+ this.obj:Fire("OnClose")
+ end
+
+ local function closeOnClick(this)
+ this.obj:Hide()
+ end
+
+ local function frameOnMouseDown(this)
+ AceGUI:ClearFocus()
+ end
+
+ local function titleOnMouseDown(this)
+ this:GetParent():StartMoving()
+ AceGUI:ClearFocus()
+ 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")
+ AceGUI:ClearFocus()
+ end
+
+ local function sizersOnMouseDown(this)
+ this:GetParent():StartSizing("BOTTOM")
+ AceGUI:ClearFocus()
+ end
+
+ local function sizereOnMouseDown(this)
+ this:GetParent():StartSizing("RIGHT")
+ AceGUI:ClearFocus()
+ 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 OnAcquire(self)
+ self.frame:SetParent(UIParent)
+ self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
+ self:ApplyStatus()
+ self:EnableResize(true)
+ end
+
+ local function OnRelease(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
+ self:SetWidth(status.width or 700)
+ self: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 - 34
+ 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 EnableResize(self, state)
+ local func = state and "Show" or "Hide"
+ self.sizer_se[func](self.sizer_se)
+ self.sizer_s[func](self.sizer_s)
+ self.sizer_e[func](self.sizer_e)
+ end
+
+ local function Constructor()
+ local frame = CreateFrame("Frame",nil,UIParent)
+ local self = {}
+ self.type = "Window"
+
+ self.Hide = Hide
+ self.Show = Show
+ self.SetTitle = SetTitle
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+ self.SetStatusText = SetStatusText
+ self.SetStatusTable = SetStatusTable
+ self.ApplyStatus = ApplyStatus
+ self.OnWidthSet = OnWidthSet
+ self.OnHeightSet = OnHeightSet
+ self.EnableResize = EnableResize
+
+ 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:SetScript("OnMouseDown", frameOnMouseDown)
+
+ frame:SetScript("OnHide",frameOnClose)
+ frame:SetMinResize(240,240)
+ frame:SetToplevel(true)
+
+ local titlebg = frame:CreateTexture(nil, "BACKGROUND")
+ titlebg:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Title-Background]])
+ titlebg:SetPoint("TOPLEFT", 9, -6)
+ titlebg:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", -28, -24)
+
+ local dialogbg = frame:CreateTexture(nil, "BACKGROUND")
+ dialogbg:SetTexture([[Interface\Tooltips\UI-Tooltip-Background]])
+ dialogbg:SetPoint("TOPLEFT", 8, -24)
+ dialogbg:SetPoint("BOTTOMRIGHT", -6, 8)
+ dialogbg:SetVertexColor(0, 0, 0, .75)
+
+ local topleft = frame:CreateTexture(nil, "BORDER")
+ topleft:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ topleft:SetWidth(64)
+ topleft:SetHeight(64)
+ topleft:SetPoint("TOPLEFT")
+ topleft:SetTexCoord(0.501953125, 0.625, 0, 1)
+
+ local topright = frame:CreateTexture(nil, "BORDER")
+ topright:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ topright:SetWidth(64)
+ topright:SetHeight(64)
+ topright:SetPoint("TOPRIGHT")
+ topright:SetTexCoord(0.625, 0.75, 0, 1)
+
+ local top = frame:CreateTexture(nil, "BORDER")
+ top:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ top:SetHeight(64)
+ top:SetPoint("TOPLEFT", topleft, "TOPRIGHT")
+ top:SetPoint("TOPRIGHT", topright, "TOPLEFT")
+ top:SetTexCoord(0.25, 0.369140625, 0, 1)
+
+ local bottomleft = frame:CreateTexture(nil, "BORDER")
+ bottomleft:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ bottomleft:SetWidth(64)
+ bottomleft:SetHeight(64)
+ bottomleft:SetPoint("BOTTOMLEFT")
+ bottomleft:SetTexCoord(0.751953125, 0.875, 0, 1)
+
+ local bottomright = frame:CreateTexture(nil, "BORDER")
+ bottomright:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ bottomright:SetWidth(64)
+ bottomright:SetHeight(64)
+ bottomright:SetPoint("BOTTOMRIGHT")
+ bottomright:SetTexCoord(0.875, 1, 0, 1)
+
+ local bottom = frame:CreateTexture(nil, "BORDER")
+ bottom:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ bottom:SetHeight(64)
+ bottom:SetPoint("BOTTOMLEFT", bottomleft, "BOTTOMRIGHT")
+ bottom:SetPoint("BOTTOMRIGHT", bottomright, "BOTTOMLEFT")
+ bottom:SetTexCoord(0.376953125, 0.498046875, 0, 1)
+
+ local left = frame:CreateTexture(nil, "BORDER")
+ left:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ left:SetWidth(64)
+ left:SetPoint("TOPLEFT", topleft, "BOTTOMLEFT")
+ left:SetPoint("BOTTOMLEFT", bottomleft, "TOPLEFT")
+ left:SetTexCoord(0.001953125, 0.125, 0, 1)
+
+ local right = frame:CreateTexture(nil, "BORDER")
+ right:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
+ right:SetWidth(64)
+ right:SetPoint("TOPRIGHT", topright, "BOTTOMRIGHT")
+ right:SetPoint("BOTTOMRIGHT", bottomright, "TOPRIGHT")
+ right:SetTexCoord(0.1171875, 0.2421875, 0, 1)
+
+ local close = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
+ close:SetPoint("TOPRIGHT", 2, 1)
+ close:SetScript("OnClick", closeOnClick)
+ self.closebutton = close
+ close.obj = self
+
+ local titletext = frame:CreateFontString(nil, "ARTWORK")
+ titletext:SetFontObject(GameFontNormal)
+ titletext:SetPoint("TOPLEFT", 12, -8)
+ titletext:SetPoint("TOPRIGHT", -32, -8)
+ self.titletext = titletext
+
+ local title = CreateFrame("Button", nil, frame)
+ title:SetPoint("TOPLEFT", titlebg)
+ title:SetPoint("BOTTOMRIGHT", titlebg)
+ title:EnableMouse()
+ title:SetScript("OnMouseDown",titleOnMouseDown)
+ title:SetScript("OnMouseUp", frameOnMouseUp)
+ self.title = title
+
+ 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",12,-32)
+ content:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-12,13)
+
+ AceGUI:RegisterAsContainer(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(Type,Constructor,Version)
+end
diff --git a/Libs/AceLocale-3.0/AceLocale-3.0.lua b/Libs/AceLocale-3.0/AceLocale-3.0.lua
new file mode 100644
index 0000000..9fc3c15
--- /dev/null
+++ b/Libs/AceLocale-3.0/AceLocale-3.0.lua
@@ -0,0 +1,136 @@
+--- **AceLocale-3.0** manages localization in addons, allowing for multiple locale to be registered with fallback to the base locale for untranslated strings.
+-- @class file
+-- @name AceLocale-3.0
+-- @release $Id: AceLocale-3.0.lua 895 2009-12-06 16:28:55Z nevcairiel $
+local MAJOR,MINOR = "AceLocale-3.0", 2
+
+local AceLocale, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceLocale then return end -- no upgrade needed
+
+-- Lua APIs
+local assert, tostring, error = assert, tostring, error
+local setmetatable, rawset, rawget = setmetatable, rawset, rawget
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GAME_LOCALE, geterrorhandler
+
+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
+ rawset(self, key, key) -- only need to see the warning once, really
+ geterrorhandler()(MAJOR..": "..tostring(AceLocale.appnames[self])..": Missing entry for '"..tostring(key).."'")
+ return key
+ end
+}
+
+-- This metatable is used on all tables returned from GetLocale if the silent flag is true, it does not issue a warning on unknown keys
+local readmetasilent = {
+ __index = function(self, key) -- requesting totally unknown entries: return key
+ rawset(self, key, key) -- only need to invoke this function once
+ return key
+ end
+}
+
+-- Remember the locale table being registered right now (it gets set by :NewLocale())
+-- NOTE: Do never try to register 2 locale tables at once and mix their definition.
+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
+})
+
+--- Register a new locale (or extend an existing one) for the specified application.
+-- :NewLocale will return a table you can fill your locale into, or nil if the locale isn't needed for the players
+-- game locale.
+-- @paramsig application, locale[, isDefault[, silent]]
+-- @param application Unique name of addon / module
+-- @param locale Name of the locale to register, e.g. "enUS", "deDE", etc.
+-- @param isDefault If this is the default locale being registered (your addon is written in this language, generally enUS)
+-- @param silent If true, the locale will not issue warnings for missing keys. Can only be set on the default locale.
+-- @usage
+-- -- enUS.lua
+-- local L = LibStub("AceLocale-3.0"):NewLocale("TestLocale", "enUS", true)
+-- L["string1"] = true
+--
+-- -- deDE.lua
+-- local L = LibStub("AceLocale-3.0"):NewLocale("TestLocale", "deDE")
+-- if not L then return end
+-- L["string1"] = "Zeichenkette1"
+-- @return Locale Table to add localizations to, or nil if the current locale is not required.
+function AceLocale:NewLocale(application, locale, isDefault, silent)
+
+ if silent and not isDefault then
+ error("Usage: NewLocale(application, locale[, isDefault[, silent]]): 'silent' can only be specified for the default locale", 2)
+ end
+
+ -- 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({}, silent and readmetasilent or 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
+
+--- 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)
+-- @param application Unique name of addon / module
+-- @param silent If true, the locale is optional, silently return nil if it's not found (defaults to false, optional)
+-- @return The locale table for the current language.
+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
diff --git a/Libs/AceLocale-3.0/AceLocale-3.0.xml b/Libs/AceLocale-3.0/AceLocale-3.0.xml
new file mode 100644
index 0000000..d69dbb1
--- /dev/null
+++ b/Libs/AceLocale-3.0/AceLocale-3.0.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/Libs/AceTimer-3.0/AceTimer-3.0.lua b/Libs/AceTimer-3.0/AceTimer-3.0.lua
new file mode 100644
index 0000000..1f40e1e
--- /dev/null
+++ b/Libs/AceTimer-3.0/AceTimer-3.0.lua
@@ -0,0 +1,473 @@
+--- **AceTimer-3.0** provides a central facility for registering timers.
+-- AceTimer supports one-shot timers and repeating timers. All timers are stored in an efficient
+-- data structure that allows easy dispatching and fast rescheduling. Timers can be registered, rescheduled
+-- or canceled at any time, even from within a running timer, without conflict or large overhead.\\
+-- AceTimer is currently limited to firing timers at a frequency of 0.1s. This constant may change
+-- in the future, but for now it seemed like a good compromise in efficiency and accuracy.
+--
+-- All `:Schedule` functions will return a handle to the current timer, which you will need to store if you
+-- need to cancel or reschedule the timer you just registered.
+--
+-- **AceTimer-3.0** can be embeded into your addon, either explicitly by calling AceTimer:Embed(MyAddon) or by
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceTimer itself.\\
+-- It is recommended to embed AceTimer, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceTimer.
+-- @class file
+-- @name AceTimer-3.0
+-- @release $Id: AceTimer-3.0.lua 895 2009-12-06 16:28:55Z nevcairiel $
+
+--[[
+ Basic assumptions:
+ * In a typical system, we do more re-scheduling per second than there are timer pulses per second
+ * Regardless of timer implementation, we cannot guarantee timely delivery due to FPS restriction (may be as low as 10)
+
+ This implementation:
+ CON: The smallest timer interval is constrained by HZ (currently 1/10s).
+ PRO: It will still correctly fire any timer slower than HZ over a length of time, e.g. 0.11s interval -> 90 times over 10 seconds
+ PRO: In lag bursts, the system simly skips missed timer intervals to decrease load
+ CON: Algorithms depending on a timer firing "N times per minute" will fail
+ PRO: (Re-)scheduling is O(1) with a VERY small constant. It's a simple linked list insertion in a hash bucket.
+ CAUTION: The BUCKETS constant constrains how many timers can be efficiently handled. With too many hash collisions, performance will decrease.
+
+ Major assumptions upheld:
+ - ALLOWS scheduling multiple timers with the same funcref/method
+ - ALLOWS scheduling more timers during OnUpdate processing
+ - ALLOWS unscheduling ANY timer (including the current running one) at any time, including during OnUpdate processing
+]]
+
+local MAJOR, MINOR = "AceTimer-3.0", 5
+local AceTimer, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceTimer then return end -- No upgrade needed
+
+AceTimer.hash = AceTimer.hash or {} -- Array of [0..BUCKET-1] = linked list of timers (using .next member)
+ -- Linked list gets around ACE-88 and ACE-90.
+AceTimer.selfs = AceTimer.selfs or {} -- Array of [self]={[handle]=timerobj, [handle2]=timerobj2, ...}
+AceTimer.frame = AceTimer.frame or CreateFrame("Frame", "AceTimer30Frame")
+
+-- Lua APIs
+local assert, error, loadstring = assert, error, loadstring
+local setmetatable, rawset, rawget = setmetatable, rawset, rawget
+local select, pairs, type, next, tostring = select, pairs, type, next, tostring
+local floor, max, min = math.floor, math.max, math.min
+local tconcat = table.concat
+
+-- WoW APIs
+local GetTime = GetTime
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: DEFAULT_CHAT_FRAME, geterrorhandler
+
+-- Simple ONE-SHOT timer cache. Much more efficient than a full compost for our purposes.
+local timerCache = nil
+
+--[[
+ Timers will not be fired more often than HZ-1 times per second.
+ Keep at intended speed PLUS ONE or we get bitten by floating point rounding errors (n.5 + 0.1 can be n.599999)
+ If this is ever LOWERED, all existing timers need to be enforced to have a delay >= 1/HZ on lib upgrade.
+ If this number is ever changed, all entries need to be rehashed on lib upgrade.
+ ]]
+local HZ = 11
+
+--[[
+ Prime for good distribution
+ If this number is ever changed, all entries need to be rehashed on lib upgrade.
+]]
+local BUCKETS = 131
+
+local hash = AceTimer.hash
+for i=1,BUCKETS do
+ hash[i] = hash[i] or false -- make it an integer-indexed array; it's faster than hashes
+end
+
+--[[
+ xpcall safecall implementation
+]]
+local xpcall = xpcall
+
+local function errorhandler(err)
+ return geterrorhandler()(err)
+end
+
+local function CreateDispatcher(argCount)
+ local code = [[
+ local xpcall, eh = ... -- our arguments are received as unnamed values in "..." since we don't have a proper function declaration
+ 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, ...)
+ return Dispatchers[select('#', ...)](func, ...)
+end
+
+local lastint = floor(GetTime() * HZ)
+
+-- --------------------------------------------------------------------
+-- OnUpdate handler
+--
+-- traverse buckets, always chasing "now", and fire timers that have expired
+
+local function OnUpdate()
+ local now = GetTime()
+ local nowint = floor(now * HZ)
+
+ -- Have we passed into a new hash bucket?
+ if nowint == lastint then return end
+
+ local soon = now + 1 -- +1 is safe as long as 1 < HZ < BUCKETS/2
+
+ -- Pass through each bucket at most once
+ -- Happens on e.g. instance loads, but COULD happen on high local load situations also
+ for curint = (max(lastint, nowint - BUCKETS) + 1), nowint do -- loop until we catch up with "now", usually only 1 iteration
+ local curbucket = (curint % BUCKETS)+1
+ -- Yank the list of timers out of the bucket and empty it. This allows reinsertion in the currently-processed bucket from callbacks.
+ local nexttimer = hash[curbucket]
+ hash[curbucket] = false -- false rather than nil to prevent the array from becoming a hash
+
+ while nexttimer do
+ local timer = nexttimer
+ nexttimer = timer.next
+ local when = timer.when
+
+ if when < soon then
+ -- Call the timer func, either as a method on given object, or a straight function ref
+ local callback = timer.callback
+ if type(callback) == "string" then
+ safecall(timer.object[callback], timer.object, timer.arg)
+ elseif callback then
+ safecall(callback, timer.arg)
+ else
+ -- probably nilled out by CancelTimer
+ timer.delay = nil -- don't reschedule it
+ end
+
+ local delay = timer.delay -- NOW make a local copy, can't do it earlier in case the timer cancelled itself in the callback
+
+ if not delay then
+ -- single-shot timer (or cancelled)
+ AceTimer.selfs[timer.object][tostring(timer)] = nil
+ timerCache = timer
+ else
+ -- repeating timer
+ local newtime = when + delay
+ if newtime < now then -- Keep lag from making us firing a timer unnecessarily. (Note that this still won't catch too-short-delay timers though.)
+ newtime = now + delay
+ end
+ timer.when = newtime
+
+ -- add next timer execution to the correct bucket
+ local bucket = (floor(newtime * HZ) % BUCKETS) + 1
+ timer.next = hash[bucket]
+ hash[bucket] = timer
+ end
+ else -- if when>=soon
+ -- reinsert (yeah, somewhat expensive, but shouldn't be happening too often either due to hash distribution)
+ timer.next = hash[curbucket]
+ hash[curbucket] = timer
+ end -- if whenhandle->timer registry
+ local handle = tostring(timer)
+
+ local selftimers = AceTimer.selfs[self]
+ if not selftimers then
+ selftimers = {}
+ AceTimer.selfs[self] = selftimers
+ end
+ selftimers[handle] = timer
+ selftimers.__ops = (selftimers.__ops or 0) + 1
+
+ return handle
+end
+
+--- Schedule a new one-shot timer.
+-- The timer will fire once in `delay` seconds, unless canceled before.
+-- @param callback Callback function for the timer pulse (funcref or method name).
+-- @param delay Delay for the timer, in seconds.
+-- @param arg An optional argument to be passed to the callback function.
+-- @usage
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("TimerTest", "AceTimer-3.0")
+--
+-- function MyAddon:OnEnable()
+-- self:ScheduleTimer("TimerFeedback", 5)
+-- end
+--
+-- function MyAddon:TimerFeedback()
+-- print("5 seconds passed")
+-- end
+function AceTimer:ScheduleTimer(callback, delay, arg)
+ return Reg(self, callback, delay, arg)
+end
+
+--- Schedule a repeating timer.
+-- The timer will fire every `delay` seconds, until canceled.
+-- @param callback Callback function for the timer pulse (funcref or method name).
+-- @param delay Delay for the timer, in seconds.
+-- @param arg An optional argument to be passed to the callback function.
+-- @usage
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("TimerTest", "AceTimer-3.0")
+--
+-- function MyAddon:OnEnable()
+-- self.timerCount = 0
+-- self.testTimer = self:ScheduleRepeatingTimer("TimerFeedback", 5)
+-- end
+--
+-- function MyAddon:TimerFeedback()
+-- self.timerCount = self.timerCount + 1
+-- print(("%d seconds passed"):format(5 * self.timerCount))
+-- -- run 30 seconds in total
+-- if self.timerCount == 6 then
+-- self:CancelTimer(self.testTimer)
+-- end
+-- end
+function AceTimer:ScheduleRepeatingTimer(callback, delay, arg)
+ return Reg(self, callback, delay, arg, true)
+end
+
+--- Cancels a timer with the given handle, registered by the same addon object as used for `:ScheduleTimer`
+-- Both one-shot and repeating timers can be canceled with this function, as long as the `handle` is valid
+-- and the timer has not fired yet or was canceled before.
+-- @param handle The handle of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
+-- @param silent If true, no error is raised if the timer handle is invalid (expired or already canceled)
+-- @return True if the timer was successfully cancelled.
+function AceTimer:CancelTimer(handle, silent)
+ if not handle then return end -- nil handle -> bail out without erroring
+ if type(handle) ~= "string" then
+ error(MAJOR..": CancelTimer(handle): 'handle' - expected a string", 2) -- for now, anyway
+ end
+ local selftimers = AceTimer.selfs[self]
+ local timer = selftimers and selftimers[handle]
+ if silent then
+ if timer then
+ timer.callback = nil -- don't run it again
+ timer.delay = nil -- if this is the currently-executing one: don't even reschedule
+ -- The timer object is removed in the OnUpdate loop
+ end
+ return not not timer -- might return "true" even if we double-cancel. we'll live.
+ else
+ if not timer then
+ geterrorhandler()(MAJOR..": CancelTimer(handle[, silent]): '"..tostring(handle).."' - no such timer registered")
+ return false
+ end
+ if not timer.callback then
+ geterrorhandler()(MAJOR..": CancelTimer(handle[, silent]): '"..tostring(handle).."' - timer already cancelled or expired")
+ return false
+ end
+ timer.callback = nil -- don't run it again
+ timer.delay = nil -- if this is the currently-executing one: don't even reschedule
+ return true
+ end
+end
+
+--- Cancels all timers registered to the current addon object ('self')
+function AceTimer:CancelAllTimers()
+ if not(type(self) == "string" or type(self) == "table") then
+ error(MAJOR..": CancelAllTimers(): 'self' - must be a string or a table",2)
+ end
+ if self == AceTimer then
+ error(MAJOR..": CancelAllTimers(): supply a meaningful 'self'", 2)
+ end
+
+ local selftimers = AceTimer.selfs[self]
+ if selftimers then
+ for handle,v in pairs(selftimers) do
+ if type(v) == "table" then -- avoid __ops, etc
+ AceTimer.CancelTimer(self, handle, true)
+ end
+ end
+ end
+end
+
+--- Returns the time left for a timer with the given handle, registered by the current addon object ('self').
+-- This function will raise a warning when the handle is invalid, but not stop execution.
+-- @param handle The handle of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
+-- @return The time left on the timer, or false if the handle is invalid.
+function AceTimer:TimeLeft(handle)
+ if not handle then return end
+ if type(handle) ~= "string" then
+ error(MAJOR..": TimeLeft(handle): 'handle' - expected a string", 2) -- for now, anyway
+ end
+ local selftimers = AceTimer.selfs[self]
+ local timer = selftimers and selftimers[handle]
+ if not timer then
+ geterrorhandler()(MAJOR..": TimeLeft(handle): '"..tostring(handle).."' - no such timer registered")
+ return false
+ end
+ return timer.when - GetTime()
+end
+
+
+-- ---------------------------------------------------------------------
+-- PLAYER_REGEN_ENABLED: Run through our .selfs[] array step by step
+-- and clean it out - otherwise the table indices can grow indefinitely
+-- if an addon starts and stops a lot of timers. AceBucket does this!
+--
+-- See ACE-94 and tests/AceTimer-3.0-ACE-94.lua
+
+local lastCleaned = nil
+
+local function OnEvent(this, event)
+ if event~="PLAYER_REGEN_ENABLED" then
+ return
+ end
+
+ -- Get the next 'self' to process
+ local selfs = AceTimer.selfs
+ local self = next(selfs, lastCleaned)
+ if not self then
+ self = next(selfs)
+ end
+ lastCleaned = self
+ if not self then -- should only happen if .selfs[] is empty
+ return
+ end
+
+ -- Time to clean it out?
+ local list = selfs[self]
+ if (list.__ops or 0) < 250 then -- 250 slosh indices = ~10KB wasted (max!). For one 'self'.
+ return
+ end
+
+ -- Create a new table and copy all members over
+ local newlist = {}
+ local n=0
+ for k,v in pairs(list) do
+ newlist[k] = v
+ n=n+1
+ end
+ newlist.__ops = 0 -- Reset operation count
+
+ -- And since we now have a count of the number of live timers, check that it's reasonable. Emit a warning if not.
+ if n>BUCKETS then
+ DEFAULT_CHAT_FRAME:AddMessage(MAJOR..": Warning: The addon/module '"..tostring(self).."' has "..n.." live timers. Surely that's not intended?")
+ end
+
+ selfs[self] = newlist
+end
+
+-- ---------------------------------------------------------------------
+-- Embed handling
+
+AceTimer.embeds = AceTimer.embeds or {}
+
+local mixins = {
+ "ScheduleTimer", "ScheduleRepeatingTimer",
+ "CancelTimer", "CancelAllTimers",
+ "TimeLeft"
+}
+
+function AceTimer:Embed(target)
+ AceTimer.embeds[target] = true
+ for _,v in pairs(mixins) do
+ target[v] = AceTimer[v]
+ end
+ return target
+end
+
+-- AceTimer:OnEmbedDisable( target )
+-- target (object) - target object that AceTimer is embedded in.
+--
+-- cancel all timers registered for the object
+function AceTimer:OnEmbedDisable( target )
+ target:CancelAllTimers()
+end
+
+
+for addon in pairs(AceTimer.embeds) do
+ AceTimer:Embed(addon)
+end
+
+-- ---------------------------------------------------------------------
+-- Debug tools (expose copies of internals to test suites)
+AceTimer.debug = AceTimer.debug or {}
+AceTimer.debug.HZ = HZ
+AceTimer.debug.BUCKETS = BUCKETS
+
+-- ---------------------------------------------------------------------
+-- Finishing touchups
+
+AceTimer.frame:SetScript("OnUpdate", OnUpdate)
+AceTimer.frame:SetScript("OnEvent", OnEvent)
+AceTimer.frame:RegisterEvent("PLAYER_REGEN_ENABLED")
+
+-- In theory, we should hide&show the frame based on there being timers or not.
+-- However, this job is fairly expensive, and the chance that there will
+-- actually be zero timers running is diminuitive to say the lest.
diff --git a/Libs/AceTimer-3.0/AceTimer-3.0.xml b/Libs/AceTimer-3.0/AceTimer-3.0.xml
new file mode 100644
index 0000000..7c478a4
--- /dev/null
+++ b/Libs/AceTimer-3.0/AceTimer-3.0.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua b/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua
new file mode 100644
index 0000000..5a495f6
--- /dev/null
+++ b/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua
@@ -0,0 +1,239 @@
+--[[ $Id: CallbackHandler-1.0.lua 3 2008-09-29 16:54: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.
+
diff --git a/Libs/CallbackHandler-1.0/CallbackHandler-1.0.xml b/Libs/CallbackHandler-1.0/CallbackHandler-1.0.xml
new file mode 100644
index 0000000..1aad3a2
--- /dev/null
+++ b/Libs/CallbackHandler-1.0/CallbackHandler-1.0.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua b/Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua
new file mode 100644
index 0000000..5933455
--- /dev/null
+++ b/Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua
@@ -0,0 +1,263 @@
+--[[
+Name: DBIcon-1.0
+Revision: $Rev: 12 $
+Author(s): Rabbit (rabbit.magtheridon@gmail.com)
+Description: Allows addons to register to recieve a lightweight minimap icon as an alternative to more heavy LDB displays.
+Dependencies: LibStub
+License: GPL v2 or later.
+]]
+
+--[[
+Copyright (C) 2008-2009 Rabbit
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+]]
+
+-----------------------------------------------------------------------
+-- DBIcon-1.0
+--
+-- Disclaimer: Most of this code was ripped from Barrel but fixed, streamlined
+-- and cleaned up a lot so that it no longer sucks.
+--
+
+local DBICON10 = "LibDBIcon-1.0"
+local DBICON10_MINOR = tonumber(("$Rev: 12 $"):match("(%d+)"))
+if not LibStub then error(DBICON10 .. " requires LibStub.") end
+local ldb = LibStub("LibDataBroker-1.1", true)
+if not ldb then error(DBICON10 .. " requires LibDataBroker-1.1.") end
+local lib = LibStub:NewLibrary(DBICON10, DBICON10_MINOR)
+if not lib then return end
+
+lib.disabled = lib.disabled or nil
+lib.objects = lib.objects or {}
+lib.callbackRegistered = lib.callbackRegistered or nil
+lib.notCreated = lib.notCreated or {}
+
+function lib:IconCallback(event, name, key, value, dataobj)
+ if lib.objects[name] then
+ lib.objects[name].icon:SetTexture(dataobj.icon)
+ end
+end
+if not lib.callbackRegistered then
+ ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__icon", "IconCallback")
+ lib.callbackRegistered = true
+end
+
+-- Tooltip code ripped from StatBlockCore by Funkydude
+local function getAnchors(frame)
+ local x,y = frame:GetCenter()
+ if not x or not y then return "TOPLEFT", "BOTTOMLEFT" end
+ local hhalf = (x > UIParent:GetWidth()*2/3) and "RIGHT" or (x < UIParent:GetWidth()/3) and "LEFT" or ""
+ local vhalf = (y > UIParent:GetHeight()/2) and "TOP" or "BOTTOM"
+ return vhalf..hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP")..hhalf
+end
+
+local function onEnter(self)
+ if self.isMoving then return end
+ local obj = self.dataObject
+ if obj.OnTooltipShow then
+ GameTooltip:SetOwner(self, "ANCHOR_NONE")
+ GameTooltip:SetPoint(getAnchors(self))
+ obj.OnTooltipShow(GameTooltip)
+ GameTooltip:Show()
+ elseif obj.OnEnter then
+ obj.OnEnter(self)
+ end
+end
+
+local function onLeave(self)
+ local obj = self.dataObject
+ GameTooltip:Hide()
+ if obj.OnLeave then obj.OnLeave(self) end
+end
+
+--------------------------------------------------------------------------------
+
+local minimapShapes = {
+ ["ROUND"] = {true, true, true, true},
+ ["SQUARE"] = {false, false, false, false},
+ ["CORNER-TOPLEFT"] = {true, false, false, false},
+ ["CORNER-TOPRIGHT"] = {false, false, true, false},
+ ["CORNER-BOTTOMLEFT"] = {false, true, false, false},
+ ["CORNER-BOTTOMRIGHT"] = {false, false, false, true},
+ ["SIDE-LEFT"] = {true, true, false, false},
+ ["SIDE-RIGHT"] = {false, false, true, true},
+ ["SIDE-TOP"] = {true, false, true, false},
+ ["SIDE-BOTTOM"] = {false, true, false, true},
+ ["TRICORNER-TOPLEFT"] = {true, true, true, false},
+ ["TRICORNER-TOPRIGHT"] = {true, false, true, true},
+ ["TRICORNER-BOTTOMLEFT"] = {true, true, false, true},
+ ["TRICORNER-BOTTOMRIGHT"] = {false, true, true, true},
+}
+
+local function updatePosition(button)
+ local angle = math.rad(button.db.minimapPos or 225)
+ local x, y, q = math.cos(angle), math.sin(angle), 1
+ if x < 0 then q = q + 1 end
+ if y > 0 then q = q + 2 end
+ local minimapShape = GetMinimapShape and GetMinimapShape() or "ROUND"
+ local quadTable = minimapShapes[minimapShape]
+ if quadTable[q] then
+ x, y = x*80, y*80
+ else
+ local diagRadius = 103.13708498985 --math.sqrt(2*(80)^2)-10
+ x = math.max(-80, math.min(x*diagRadius, 80))
+ y = math.max(-80, math.min(y*diagRadius, 80))
+ end
+ button:SetPoint("CENTER", Minimap, "CENTER", x, y)
+end
+
+local function onClick(self, b) if self.dataObject.OnClick then self.dataObject.OnClick(self, b) end end
+local function onMouseDown(self) self.icon:SetTexCoord(0, 1, 0, 1) end
+local function onMouseUp(self) self.icon:SetTexCoord(0.05, 0.95, 0.05, 0.95) end
+
+local function onUpdate(self)
+ local mx, my = Minimap:GetCenter()
+ local px, py = GetCursorPosition()
+ local scale = Minimap:GetEffectiveScale()
+ px, py = px / scale, py / scale
+ self.db.minimapPos = math.deg(math.atan2(py - my, px - mx)) % 360
+ updatePosition(self)
+end
+
+local function onDragStart(self)
+ self:LockHighlight()
+ self.icon:SetTexCoord(0, 1, 0, 1)
+ self:SetScript("OnUpdate", onUpdate)
+ self.isMoving = true
+ GameTooltip:Hide()
+end
+
+local function onDragStop(self)
+ self:SetScript("OnUpdate", nil)
+ self.icon:SetTexCoord(0.05, 0.95, 0.05, 0.95)
+ self:UnlockHighlight()
+ self.isMoving = nil
+end
+
+local function createButton(name, object, db)
+ local button = CreateFrame("Button", "LibDBIcon10_"..name, Minimap)
+ button.dataObject = object
+ button.db = db
+ button:SetFrameStrata("MEDIUM")
+ button:SetWidth(31); button:SetHeight(31)
+ button:SetFrameLevel(8)
+ button:RegisterForClicks("anyUp")
+ button:RegisterForDrag("LeftButton")
+ button:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight")
+ local overlay = button:CreateTexture(nil, "OVERLAY")
+ overlay:SetWidth(53); overlay:SetHeight(53)
+ overlay:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder")
+ overlay:SetPoint("TOPLEFT")
+ local icon = button:CreateTexture(nil, "BACKGROUND")
+ icon:SetWidth(20); icon:SetHeight(20)
+ icon:SetTexture(object.icon)
+ icon:SetTexCoord(0.05, 0.95, 0.05, 0.95)
+ icon:SetPoint("TOPLEFT", 7, -5)
+ button.icon = icon
+
+ button:SetScript("OnEnter", onEnter)
+ button:SetScript("OnLeave", onLeave)
+ button:SetScript("OnClick", onClick)
+ button:SetScript("OnDragStart", onDragStart)
+ button:SetScript("OnDragStop", onDragStop)
+ button:SetScript("OnMouseDown", onMouseDown)
+ button:SetScript("OnMouseUp", onMouseUp)
+
+ lib.objects[name] = button
+
+ if lib.loggedIn then
+ updatePosition(button)
+ if not db.hide then button:Show()
+ else button:Hide() end
+ end
+end
+
+-- We could use a metatable.__index on lib.objects, but then we'd create
+-- the icons when checking things like :IsRegistered, which is not necessary.
+local function check(name)
+ if lib.notCreated[name] then
+ createButton(name, lib.notCreated[name][1], lib.notCreated[name][2])
+ lib.notCreated[name] = nil
+ end
+end
+
+lib.loggedIn = lib.loggedIn or false
+-- Wait a bit with the initial positioning to let any GetMinimapShape addons
+-- load up.
+if not lib.loggedIn then
+ local f = CreateFrame("Frame")
+ f:SetScript("OnEvent", function()
+ for _, object in pairs(lib.objects) do
+ updatePosition(object)
+ if not lib.disabled and not object.db.hide then object:Show()
+ else object:Hide() end
+ end
+ lib.loggedIn = true
+ f:SetScript("OnEvent", nil)
+ f = nil
+ end)
+ f:RegisterEvent("PLAYER_LOGIN")
+end
+
+function lib:Register(name, object, db)
+ if lib.disabled then return end
+ if not object.icon then error("Can't register LDB objects without icons set!") end
+ if lib.objects[name] or lib.notCreated[name] then error("Already registered, nubcake.") end
+ if not db or not db.hide then
+ createButton(name, object, db)
+ else
+ lib.notCreated[name] = {object, db}
+ end
+end
+
+function lib:Hide(name)
+ if not lib.objects[name] then return end
+ lib.objects[name]:Hide()
+end
+function lib:Show(name)
+ if lib.disabled then return end
+ check(name)
+ lib.objects[name]:Show()
+ updatePosition(lib.objects[name])
+end
+function lib:IsRegistered(name)
+ return (lib.objects[name] or lib.notCreated[name]) and true or false
+end
+function lib:Refresh(name, db)
+ if lib.disabled then return end
+ check(name)
+ local button = lib.objects[name]
+ if db then button.db = db end
+ updatePosition(button)
+end
+
+function lib:EnableLibrary()
+ lib.disabled = nil
+ for name, object in pairs(lib.objects) do
+ if not object.db or (object.db and not object.db.hide) then
+ object:Show()
+ updatePosition(object)
+ end
+ end
+end
+
+function lib:DisableLibrary()
+ lib.disabled = true
+ for name, object in pairs(lib.objects) do
+ object:Hide()
+ end
+end
+
diff --git a/Libs/LibDataBroker-1.1/LibDataBroker-1.1.lua b/Libs/LibDataBroker-1.1/LibDataBroker-1.1.lua
index 364602e..04b5037 100644
--- a/Libs/LibDataBroker-1.1/LibDataBroker-1.1.lua
+++ b/Libs/LibDataBroker-1.1/LibDataBroker-1.1.lua
@@ -1,66 +1,66 @@
-
-assert(LibStub, "LibDataBroker-1.1 requires LibStub")
-assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0")
-
-local lib, oldminor = LibStub:NewLibrary("LibDataBroker-1.1", 3)
-if not lib then return end
-oldminor = oldminor or 0
-
-
-lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib)
-lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {}
-local attributestorage, namestorage, callbacks = lib.attributestorage, lib.namestorage, lib.callbacks
-
-if oldminor < 2 then
- lib.domt = {
- __metatable = "access denied",
- __index = function(self, key) return attributestorage[self] and attributestorage[self][key] end,
- }
-end
-
-if oldminor < 3 then
- lib.domt.__newindex = function(self, key, value)
- if not attributestorage[self] then attributestorage[self] = {} end
- if attributestorage[self][key] == value then return end
- attributestorage[self][key] = value
- local name = namestorage[self]
- if not name then return end
- callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value, self)
- callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value, self)
- callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value, self)
- callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value, self)
- end
-end
-
-if oldminor < 2 then
- function lib:NewDataObject(name, dataobj)
- if self.proxystorage[name] then return end
-
- if dataobj then
- assert(type(dataobj) == "table", "Invalid dataobj, must be nil or a table")
- self.attributestorage[dataobj] = {}
- for i,v in pairs(dataobj) do
- self.attributestorage[dataobj][i] = v
- dataobj[i] = nil
- end
- end
- dataobj = setmetatable(dataobj or {}, self.domt)
- self.proxystorage[name], self.namestorage[dataobj] = dataobj, name
- self.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj)
- return dataobj
- end
-end
-
-if oldminor < 1 then
- function lib:DataObjectIterator()
- return pairs(self.proxystorage)
- end
-
- function lib:GetDataObjectByName(dataobjectname)
- return self.proxystorage[dataobjectname]
- end
-
- function lib:GetNameByDataObject(dataobject)
- return self.namestorage[dataobject]
- end
-end
+
+assert(LibStub, "LibDataBroker-1.1 requires LibStub")
+assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0")
+
+local lib, oldminor = LibStub:NewLibrary("LibDataBroker-1.1", 3)
+if not lib then return end
+oldminor = oldminor or 0
+
+
+lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib)
+lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {}
+local attributestorage, namestorage, callbacks = lib.attributestorage, lib.namestorage, lib.callbacks
+
+if oldminor < 2 then
+ lib.domt = {
+ __metatable = "access denied",
+ __index = function(self, key) return attributestorage[self] and attributestorage[self][key] end,
+ }
+end
+
+if oldminor < 3 then
+ lib.domt.__newindex = function(self, key, value)
+ if not attributestorage[self] then attributestorage[self] = {} end
+ if attributestorage[self][key] == value then return end
+ attributestorage[self][key] = value
+ local name = namestorage[self]
+ if not name then return end
+ callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value, self)
+ callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value, self)
+ callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value, self)
+ callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value, self)
+ end
+end
+
+if oldminor < 2 then
+ function lib:NewDataObject(name, dataobj)
+ if self.proxystorage[name] then return end
+
+ if dataobj then
+ assert(type(dataobj) == "table", "Invalid dataobj, must be nil or a table")
+ self.attributestorage[dataobj] = {}
+ for i,v in pairs(dataobj) do
+ self.attributestorage[dataobj][i] = v
+ dataobj[i] = nil
+ end
+ end
+ dataobj = setmetatable(dataobj or {}, self.domt)
+ self.proxystorage[name], self.namestorage[dataobj] = dataobj, name
+ self.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj)
+ return dataobj
+ end
+end
+
+if oldminor < 1 then
+ function lib:DataObjectIterator()
+ return pairs(self.proxystorage)
+ end
+
+ function lib:GetDataObjectByName(dataobjectname)
+ return self.proxystorage[dataobjectname]
+ end
+
+ function lib:GetNameByDataObject(dataobject)
+ return self.namestorage[dataobject]
+ end
+end
diff --git a/Libs/LibSharedMedia-3.0/LibSharedMedia-3.0.lua b/Libs/LibSharedMedia-3.0/LibSharedMedia-3.0.lua
new file mode 100644
index 0000000..ef062a0
--- /dev/null
+++ b/Libs/LibSharedMedia-3.0/LibSharedMedia-3.0.lua
@@ -0,0 +1,230 @@
+--[[
+Name: LibSharedMedia-3.0
+Revision: $Revision: 58 $
+Author: Elkano (elkano@gmx.de)
+Inspired By: SurfaceLib by Haste/Otravi (troeks@gmail.com)
+Website: http://www.wowace.com/projects/libsharedmedia-3-0/
+Description: Shared handling of media data (fonts, sounds, textures, ...) between addons.
+Dependencies: LibStub, CallbackHandler-1.0
+License: LGPL v2.1
+]]
+
+local MAJOR, MINOR = "LibSharedMedia-3.0", 90000 + tonumber(("$Revision: 58 $"):match("(%d+)"))
+local lib = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not lib then return end
+
+local _G = getfenv(0)
+
+local pairs = _G.pairs
+local type = _G.type
+
+local band = _G.bit.band
+
+local table_insert = _G.table.insert
+local table_sort = _G.table.sort
+
+local locale = GetLocale()
+local locale_is_western
+local LOCALE_MASK = 0
+lib.LOCALE_BIT_koKR = 1
+lib.LOCALE_BIT_ruRU = 2
+lib.LOCALE_BIT_zhCN = 4
+lib.LOCALE_BIT_zhTW = 8
+lib.LOCALE_BIT_western = 128
+
+local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0")
+
+lib.callbacks = lib.callbacks or CallbackHandler:New(lib)
+
+lib.DefaultMedia = lib.DefaultMedia or {}
+lib.MediaList = lib.MediaList or {}
+lib.MediaTable = lib.MediaTable or {}
+lib.MediaType = lib.MediaType or {}
+lib.OverrideMedia = lib.OverrideMedia or {}
+
+local defaultMedia = lib.DefaultMedia
+local mediaList = lib.MediaList
+local mediaTable = lib.MediaTable
+local overrideMedia = lib.OverrideMedia
+
+
+-- create mediatype constants
+lib.MediaType.BACKGROUND = "background" -- background textures
+lib.MediaType.BORDER = "border" -- border textures
+lib.MediaType.FONT = "font" -- fonts
+lib.MediaType.STATUSBAR = "statusbar" -- statusbar textures
+lib.MediaType.SOUND = "sound" -- sound files
+
+-- populate lib with default Blizzard data
+-- BACKGROUND
+if not lib.MediaTable.background then lib.MediaTable.background = {} end
+lib.MediaTable.background["Blizzard Dialog Background"] = [[Interface\DialogFrame\UI-DialogBox-Background]]
+lib.MediaTable.background["Blizzard Low Health"] = [[Interface\FullScreenTextures\LowHealth]]
+lib.MediaTable.background["Blizzard Out of Control"] = [[Interface\FullScreenTextures\OutOfControl]]
+lib.MediaTable.background["Blizzard Parchment"] = [[Interface\AchievementFrame\UI-Achievement-Parchment-Horizontal]]
+lib.MediaTable.background["Blizzard Parchment 2"] = [[Interface\AchievementFrame\UI-Achievement-Parchment]]
+lib.MediaTable.background["Blizzard Tabard Background"] = [[Interface\TabardFrame\TabardFrameBackground]]
+lib.MediaTable.background["Blizzard Tooltip"] = [[Interface\Tooltips\UI-Tooltip-Background]]
+lib.MediaTable.background["Solid"] = [[Interface\Buttons\WHITE8X8]]
+
+-- BORDER
+if not lib.MediaTable.border then lib.MediaTable.border = {} end
+lib.MediaTable.border["None"] = [[Interface\None]]
+lib.MediaTable.border["Blizzard Achievement Wood"] = [[Interface\AchievementFrame\UI-Achievement-WoodBorder]]
+lib.MediaTable.border["Blizzard Chat Bubble"] = [[Interface\Tooltips\ChatBubble-Backdrop]]
+lib.MediaTable.border["Blizzard Dialog"] = [[Interface\DialogFrame\UI-DialogBox-Border]]
+lib.MediaTable.border["Blizzard Dialog Gold"] = [[Interface\DialogFrame\UI-DialogBox-Gold-Border]]
+lib.MediaTable.border["Blizzard Party"] = [[Interface\CHARACTERFRAME\UI-Party-Border]]
+lib.MediaTable.border["Blizzard Tooltip"] = [[Interface\Tooltips\UI-Tooltip-Border]]
+
+-- FONT
+if not lib.MediaTable.font then lib.MediaTable.font = {} end
+local SML_MT_font = lib.MediaTable.font
+if locale == "koKR" then
+ LOCALE_MASK = lib.LOCALE_BIT_koKR
+--
+ SML_MT_font["굵은 글꼴"] = [[Fonts\2002B.TTF]]
+ SML_MT_font["기본 글꼴"] = [[Fonts\2002.TTF]]
+ SML_MT_font["데미지 글꼴"] = [[Fonts\K_Damage.TTF]]
+ SML_MT_font["퀘스트 글꼴"] = [[Fonts\K_Pagetext.TTF]]
+--
+ lib.DefaultMedia["font"] = "기본 글꼴" -- someone from koKR please adjust if needed
+--
+elseif locale == "zhCN" then
+ LOCALE_MASK = lib.LOCALE_BIT_zhCN
+--
+ SML_MT_font["伤害数字"] = [[Fonts\ZYKai_C.ttf]]
+ SML_MT_font["默认"] = [[Fonts\ZYKai_T.ttf]]
+ SML_MT_font["聊天"] = [[Fonts\ZYHei.ttf]]
+--
+ lib.DefaultMedia["font"] = "默认" -- someone from zhCN please adjust if needed
+--
+elseif locale == "zhTW" then
+ LOCALE_MASK = lib.LOCALE_BIT_zhTW
+--
+ SML_MT_font["提示訊息"] = [[Fonts\bHEI00M.ttf]]
+ SML_MT_font["聊天"] = [[Fonts\bHEI01B.ttf]]
+ SML_MT_font["傷害數字"] = [[Fonts\bKAI00M.ttf]]
+ SML_MT_font["預設"] = [[Fonts\bLEI00D.ttf]]
+--
+ lib.DefaultMedia["font"] = "預設" -- someone from zhTW please adjust if needed
+
+elseif locale == "ruRU" then
+ LOCALE_MASK = lib.LOCALE_BIT_ruRU
+--
+ SML_MT_font["Arial Narrow"] = [[Fonts\ARIALN.TTF]]
+ SML_MT_font["Friz Quadrata TT"] = [[Fonts\FRIZQT__.TTF]]
+ SML_MT_font["Morpheus"] = [[Fonts\MORPHEUS.TTF]]
+ SML_MT_font["Nimrod MT"] = [[Fonts\NIM_____.ttf]]
+ SML_MT_font["Skurri"] = [[Fonts\SKURRI.TTF]]
+--
+ lib.DefaultMedia.font = "Friz Quadrata TT"
+--
+else
+ LOCALE_MASK = lib.LOCALE_BIT_western
+ locale_is_western = true
+--
+ SML_MT_font["Arial Narrow"] = [[Fonts\ARIALN.TTF]]
+ SML_MT_font["Friz Quadrata TT"] = [[Fonts\FRIZQT__.TTF]]
+ SML_MT_font["Morpheus"] = [[Fonts\MORPHEUS.TTF]]
+ SML_MT_font["Skurri"] = [[Fonts\SKURRI.TTF]]
+--
+ lib.DefaultMedia.font = "Friz Quadrata TT"
+--
+end
+
+-- STATUSBAR
+if not lib.MediaTable.statusbar then lib.MediaTable.statusbar = {} end
+lib.MediaTable.statusbar["Blizzard"] = [[Interface\TargetingFrame\UI-StatusBar]]
+lib.DefaultMedia.statusbar = "Blizzard"
+
+-- SOUND
+if not lib.MediaTable.sound then lib.MediaTable.sound = {} end
+lib.MediaTable.sound["None"] = [[Interface\Quiet.mp3]] -- Relies on the fact that PlaySound[File] doesn't error on non-existing input.
+lib.DefaultMedia.sound = "None"
+
+local function rebuildMediaList(mediatype)
+ local mtable = mediaTable[mediatype]
+ if not mtable then return end
+ if not mediaList[mediatype] then mediaList[mediatype] = {} end
+ local mlist = mediaList[mediatype]
+ -- list can only get larger, so simply overwrite it
+ local i = 0
+ for k in pairs(mtable) do
+ i = i + 1
+ mlist[i] = k
+ end
+ table_sort(mlist)
+end
+
+function lib:Register(mediatype, key, data, langmask)
+ if type(mediatype) ~= "string" then
+ error(MAJOR..":Register(mediatype, key, data, langmask) - mediatype must be string, got "..type(mediatype))
+ end
+ if type(key) ~= "string" then
+ error(MAJOR..":Register(mediatype, key, data, langmask) - key must be string, got "..type(key))
+ end
+ if mediatype == lib.MediaType.FONT and ((langmask and band(langmask, LOCALE_MASK) == 0) or not (langmask or locale_is_western)) then return false end
+ mediatype = mediatype:lower()
+ if not mediaTable[mediatype] then mediaTable[mediatype] = {} end
+ local mtable = mediaTable[mediatype]
+ if mtable[key] then return false end
+
+ mtable[key] = data
+ rebuildMediaList(mediatype)
+ self.callbacks:Fire("LibSharedMedia_Registered", mediatype, key)
+ return true
+end
+
+function lib:Fetch(mediatype, key, noDefault)
+ local mtt = mediaTable[mediatype]
+ local overridekey = overrideMedia[mediatype]
+ local result = mtt and ((overridekey and mtt[overridekey] or mtt[key]) or (not noDefault and defaultMedia[mediatype] and mtt[defaultMedia[mediatype]])) or nil
+
+ return result
+end
+
+function lib:IsValid(mediatype, key)
+ return mediaTable[mediatype] and (not key or mediaTable[mediatype][key]) and true or false
+end
+
+function lib:HashTable(mediatype)
+ return mediaTable[mediatype]
+end
+
+function lib:List(mediatype)
+ if not mediaTable[mediatype] then
+ return nil
+ end
+ if not mediaList[mediatype] then
+ rebuildMediaList(mediatype)
+ end
+ return mediaList[mediatype]
+end
+
+function lib:GetGlobal(mediatype)
+ return overrideMedia[mediatype]
+end
+
+function lib:SetGlobal(mediatype, key)
+ if not mediaTable[mediatype] then
+ return false
+ end
+ overrideMedia[mediatype] = (key and mediaTable[mediatype][key]) and key or nil
+ self.callbacks:Fire("LibSharedMedia_SetGlobal", mediatype, overrideMedia[mediatype])
+ return true
+end
+
+function lib:GetDefault(mediatype)
+ return defaultMedia[mediatype]
+end
+
+function lib:SetDefault(mediatype, key)
+ if mediaTable[mediatype] and mediaTable[mediatype][key] and not defaultMedia[mediatype] then
+ defaultMedia[mediatype] = key
+ return true
+ else
+ return false
+ end
+end
diff --git a/Libs/LibSharedMedia-3.0/lib.xml b/Libs/LibSharedMedia-3.0/lib.xml
new file mode 100644
index 0000000..5b4a5c8
--- /dev/null
+++ b/Libs/LibSharedMedia-3.0/lib.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/Libs/LibSink-2.0/LibSink-2.0.lua b/Libs/LibSink-2.0/LibSink-2.0.lua
new file mode 100644
index 0000000..99ac5c2
--- /dev/null
+++ b/Libs/LibSink-2.0/LibSink-2.0.lua
@@ -0,0 +1,738 @@
+--[[
+Name: Sink-2.0
+Revision: $Rev: 67 $
+Author(s): Rabbit (rabbit.magtheridon@gmail.com), Antiarc (cheal@gmail.com)
+Website: http://rabbit.nihilum.eu
+Documentation: http://wiki.wowace.com/index.php/Sink-2.0
+SVN: http://svn.wowace.com/wowace/trunk/SinkLib/Sink-2.0
+Description: Library that handles chat output.
+Dependencies: LibStub, SharedMedia-3.0 (optional)
+License: GPL v2 or later.
+]]
+
+--[[
+Copyright (C) 2008 Rabbit
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+]]
+
+-----------------------------------------------------------------------
+-- Sink-2.0
+
+local SINK20 = "LibSink-2.0"
+local SINK20_MINOR = 90000 + tonumber(("$Revision: 67 $"):match("(%d+)"))
+
+local sink = LibStub:NewLibrary(SINK20, SINK20_MINOR)
+if not sink then return end
+
+-- Start upgrade
+sink.storageForAddon = sink.storageForAddon or {}
+sink.override = sink.override or {}
+sink.msbt_registered_fonts = sink.msbt_registered_fonts or {}
+sink.registeredScrollAreaFunctions = sink.registeredScrollAreaFunctions or {}
+sink.handlers = sink.handlers or {}
+
+sink.stickyAddons = sink.stickyAddons or {
+ Blizzard = true,
+ MikSBT = true,
+ SCT = true,
+ Parrot = true,
+ BCF = true,
+}
+
+-- Upgrade complete
+
+local L_DEFAULT = "Default"
+local L_DEFAULT_DESC = "Route output from this addon through the first available handler, preferring scrolling combat text addons if available."
+local L_ROUTE = "Route output from this addon through %s."
+local L_SCT = "Scrolling Combat Text"
+local L_MSBT = "MikSBT"
+local L_BIGWIGS = "BigWigs"
+local L_BCF = "BlinkCombatFeedback"
+local L_UIERROR = "Blizzard Error Frame"
+local L_CHAT = "Chat"
+local L_BLIZZARD = "Blizzard FCT"
+local L_RW = "Raid Warning"
+local L_PARROT = "Parrot"
+local L_CHANNEL = "Channel"
+local L_OUTPUT = "Output"
+local L_OUTPUT_DESC = "Where to route the output from this addon."
+local L_SCROLL = "Sub section"
+local L_SCROLL_DESC = "Set the sub section where messages should appear.\n\nOnly available for some output sinks."
+local L_STICKY = "Sticky"
+local L_STICKY_DESC = "Set messages from this addon to appear as sticky.\n\nOnly available for some output sinks."
+local L_NONE = "None"
+local L_NONE_DESC = "Hide all messages from this addon."
+
+local l = GetLocale()
+if l == "koKR" then
+ L_DEFAULT = "기본"
+ L_DEFAULT_DESC = "처음으로 사용 가능한 트레이너를 통해 이 애드온으로부터 출력을 보냅니다."
+ L_ROUTE = "%s|1을;를; 통해 이 애드온의 메시지를 출력합니다."
+ L_SCT = "Scrolling Combat Text"
+ L_MSBT = "MikSBT"
+ L_BIGWIGS = "BigWigs"
+ L_BCF = "블링크의 전투 메세지"
+ L_UIERROR = "블리자드 오류 창"
+ L_CHAT = "대화창"
+ L_BLIZZARD = "블리자드 FCT"
+ L_RW = "공격대 경보"
+ L_PARROT = "Parrot"
+ L_OUTPUT = "출력"
+ L_OUTPUT_DESC = "어디에 이 애드온의 메시지를 출력할지 선택합니다."
+ L_SCROLL = "스크롤 영역"
+ L_SCROLL_DESC = "메시지를 출력할 스크룰 영역을 설정합니다.\n\nParrot, SCT나 MikSBT만 사용 가능합니다."
+ L_STICKY = "점착"
+ L_STICKY_DESC = "달라붙는 것처럼 보일 이 애드온의 메시지를 설정합니다.\n\n블리자드 FCT, Parrot, SCT나 MikSBT만 사용 가능합니다."
+ L_NONE = "없음"
+ L_NONE_DESC = "이 애드온의 모든 메시지를 숨김니다."
+elseif l == "frFR" then
+ L_DEFAULT = "Par défaut"
+ L_DEFAULT_DESC = "Transmet la sortie de cet addon via le premier handler disponible, de préférence les textes de combat défilants s'il y en a."
+ L_ROUTE = "Transmet la sortie de cet addon via %s."
+ L_SCT = "Scrolling Combat Text"
+ L_MSBT = "MikSBT"
+ L_BIGWIGS = "BigWigs"
+ L_BCF = "BlinkCombatFeedback"
+ L_UIERROR = "Cadre des erreurs"
+ L_CHAT = "Fenêtre de discussion"
+ L_BLIZZARD = "TCF de Blizzard"
+ L_RW = "Avertissement raid"
+ L_PARROT = "Parrot"
+ L_CHANNEL = "Canal"
+ L_OUTPUT = "Sortie"
+ L_OUTPUT_DESC = "Destination de la sortie de cet addon."
+ L_SCROLL = "Sous-section"
+ L_SCROLL_DESC = "Définit la sous-section où les messages doivent apparaitre.\n\nDisponible uniquement dans certains cas."
+ L_STICKY = "En évidence"
+ L_STICKY_DESC = "Fait en sortie que les messages de cet addon apparaissent en évidence.\n\nDisponible uniquement dans certains cas."
+ L_NONE = "Aucun"
+ L_NONE_DESC = "Masque tous les messages provenant de cet addon."
+elseif l == "deDE" then
+ L_DEFAULT = "Voreinstellung"
+ L_DEFAULT_DESC = "Leitet die Ausgabe von diesem Addon zum ersten verfügbaren Ausgabeort, vorzugsweise Scrollende Kampf Text Addons wenn verfügbar."
+ L_ROUTE = "Schickt die Meldungen dieses Addons an %s."
+ L_SCT = "Scrolling Combat Text(SCT)"
+ L_MSBT = "MikSBT"
+ L_BIGWIGS = "BigWigs"
+ L_BCF = "BlinkCombatFeedback"
+ L_UIERROR = "Blizzard's Fehler Fenster"
+ L_CHAT = "Im Chat"
+ L_BLIZZARD = "Blizzard's schwebenden Kampftext"
+ L_RW = "Schlachtzug's Warnung"
+ L_PARROT = "Parrot"
+ L_OUTPUT = "Ausgabe"
+ L_OUTPUT_DESC = "Wohin die Meldungen des Addons gesendet werden soll."
+ L_SCROLL = "Scroll Bereich"
+ L_SCROLL_DESC = "Setzt die Scroll Bereich, wo die Meldungen erscheinen sollen.\n\nNur verfügbar für Parrot, SCT oder MikSBT."
+ L_STICKY = "Stehend"
+ L_STICKY_DESC = "Läßt Nachrichten von diesem Addon als stehende Nachrichten erscheinen.\n\nNur verfügbar für Blizzard FCT, Parrot, SCT oder MikSBT."
+ L_NONE = "Nirgends"
+ L_NONE_DESC = "Versteckt alle Meldungen von diesem Addon."
+elseif l == "zhCN" then
+ L_DEFAULT = "默认"
+ L_DEFAULT_DESC = "插件的输出方式取决于第一个可用插件,例如有 SCT 插件,则优先使用。"
+ L_ROUTE = "经由%s显示信息。"
+ L_SCT = "SCT"
+ L_MSBT = "MikSBT"
+ L_BIGWIGS = "BigWigs"
+ L_BCF = "BlinkCombatFeedback"
+ L_UIERROR = "Blizzard 错误框体"
+ L_CHAT = "聊天框体"
+ L_BLIZZARD = "系统自带滚动战斗信息"
+ L_RW = "团队警告"
+ L_PARROT = "Parrot"
+ L_CHANNEL = "频道"
+ L_OUTPUT = "输出模式"
+ L_OUTPUT_DESC = "设置显示位置。"
+ L_SCROLL = "滚动区域"
+ L_SCROLL_DESC = "设置滚动信息显示位置。\n\n只有 Parrot、SCT 及 MikSBT 支持。"
+ L_STICKY = "固定"
+ L_STICKY_DESC = "设置信息固定显示位置。\n\n只有系统自带滚动战斗信息、Parrot、SCT 及 MikSBT 支持。"
+ L_NONE = "隐藏"
+ L_NONE_DESC = "隐藏所有来自插件的信息。"
+elseif l == "zhTW" then
+ L_DEFAULT = "預設"
+ L_DEFAULT_DESC = "插件輸出經由第一個可使用的處理器顯示,如果有 SCT 的話,則優先使用。"
+ L_ROUTE = "插件輸出經由%s顯示。"
+ L_SCT = "SCT"
+ L_MSBT = "MikSBT"
+ L_BIGWIGS = "BigWigs"
+ L_BCF = "BlinkCombatFeedback"
+ L_UIERROR = "Blizzard 錯誤訊息框架"
+ L_CHAT = "聊天視窗"
+ L_BLIZZARD = "Blizzard 浮動戰鬥文字"
+ L_RW = "團隊警告"
+ L_PARROT = "Parrot"
+ L_OUTPUT = "顯示模式"
+ L_OUTPUT_DESC = "插件輸出經由哪裡顯示。"
+ L_SCROLL = "滾動區域"
+ L_SCROLL_DESC = "設定滾動訊息出現位置。\n\n只有 Parrot,SCT 及 MikSBT 有支援。"
+ L_STICKY = "固定"
+ L_STICKY_DESC = "設定使用固定訊息。\n\n只有 Blizzard 浮動戰鬥文字,Parrot,SCT 及 MikSBT 有支援。"
+ L_NONE = "隱藏"
+ L_NONE_DESC = "隱藏所有插件輸出。"
+elseif l == "ruRU" then
+ L_DEFAULT = "По умолчанию"
+ L_DEFAULT_DESC = "Маршрут вывода сообщений данного аддона через первое доступное устройство, предпочитая доступные аддоны прокрутки текста боя."
+ L_ROUTE = "Маршрут вывода сообщений данного аддона через %s."
+ L_SCT = "SCT"
+ L_MSBT = "MikSBT"
+ L_BIGWIGS = "BigWigs"
+ L_BCF = "BlinkCombatFeedback"
+ L_UIERROR = "Фрейм ошибок Blizzard"
+ L_CHAT = "Чат"
+ L_BLIZZARD = "Blizzard FCT"
+ L_RW = "Объявление рейду"
+ L_PARROT = "Parrot"
+ L_CHANNEL = "Канал"
+ L_OUTPUT = "Вывод"
+ L_OUTPUT_DESC = "Куда выводить сообщения данного аддона."
+ L_SCROLL = "Область прокрутки"
+ L_SCROLL_DESC = "Назначить область прокрутки куда должны выводиться сообщения.\n\nДоступно только для Parrotа, SCT или MikSBT."
+ L_STICKY = "Клейкий"
+ L_STICKY_DESC = "Сделать сообщения данного аддона клейкими.\n\nДоступно только для Blizzard FCT, Parrot, SCT или MikSBT."
+ L_NONE = "Нету"
+ L_NONE_DESC = "Скрыть все сообщения данного аддона."
+end
+
+local SML = LibStub("LibSharedMedia-3.0", true)
+
+local _G = getfenv(0)
+
+local function getSticky(addon)
+ return sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20Sticky or nil
+end
+
+-- Thanks to Antiarc and his Soar-1.0 library for most of the 'meat' of the
+-- sink-specific functions.
+
+local function parrot(addon, text, r, g, b, font, size, outline, sticky, loc, icon)
+ local location = sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20ScrollArea or "Notification"
+ local s = getSticky(addon) or sticky
+ Parrot:ShowMessage(text, location, s, r, g, b, font, size, outline, icon)
+end
+
+local sct_color = {}
+local function sct(addon, text, r, g, b, font, size, outline, sticky, _, icon)
+ sct_color.r, sct_color.g, sct_color.b = r, g, b
+ local loc = sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20ScrollArea or "Messages"
+ local location = (loc == "Outgoing" and SCT.FRAME1) or (loc == "Incoming" and SCT.FRAME2) or SCT.MSG
+ local s = getSticky(addon) or sticky
+ SCT:DisplayCustomEvent(text, sct_color, s, location, nil, icon)
+end
+
+local msbt_outlines = {["NORMAL"] = 1, ["OUTLINE"] = 2, ["THICKOUTLINE"] = 3}
+local function msbt(addon, text, r, g, b, font, size, outline, sticky, _, icon)
+ if font and SML and not sink.msbt_registered_fonts[font] then
+ MikSBT.RegisterFont(font, SML:Fetch("font", font))
+ sink.msbt_registered_fonts[font] = true
+ end
+ local location = sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20ScrollArea or MikSBT.DISPLAYTYPE_NOTIFICATION
+ local s = getSticky(addon) or sticky
+ MikSBT.DisplayMessage(text, location, s, r * 255, g * 255, b * 255, size, font, msbt_outlines[outline], icon)
+end
+
+local bcf_outlines = {NORMAL = "", OUTLINE = "OUTLINE", THICKOUTLINE = "THICKOUTLINE"}
+local function bcf(addon, text, r, g, b, font, size, outline, sticky, _, icon)
+ if icon then text = "|T"..icon..":20:20:-5|t"..text end
+ local loc = sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20ScrollArea or "Sticky"
+ local s = getSticky(addon) or sticky
+ BlinkCombatFeedback:DisplayCustomEvent({display = {msg = text, color = ("%02x%02x%02x"):format(r * 255, g * 255, b * 255), scrollArea = loc, scrollType = s and "Sticky" or "up", size = size, outling = bcf_outlines[outline], align = "center", font = font}})
+end
+
+local function blizzard(addon, text, r, g, b, font, size, outline, sticky, _, icon)
+ if icon then text = "|T"..icon..":20:20:-5|t"..text end
+ if tostring(SHOW_COMBAT_TEXT) ~= "0" then
+ local s = getSticky(addon) or sticky
+ CombatText_AddMessage(text, CombatText_StandardScroll, r, g, b, s and "crit" or nil, false)
+ else
+ UIErrorsFrame:AddMessage(text, r, g, b, 1.0)
+ end
+end
+
+local channelMapping = {
+ [SAY] = "SAY",
+ [PARTY] = "PARTY",
+ [BATTLEGROUND] = "BATTLEGROUND",
+ [GUILD_CHAT] = "GUILD",
+ [OFFICER_CHAT] = "OFFICER",
+ [YELL] = "YELL",
+ [RAID] = "RAID",
+ [RAID_WARNING] = "RAID_WARNING",
+}
+
+local function channel(addon, text)
+ -- Sanitize the text, remove all color codes.
+ text = text:gsub("(|c%x%x%x%x%x%x%x%x)", ""):gsub("(|r)", "")
+ local loc = sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20ScrollArea or SAY
+ SendChatMessage(text, channelMapping[loc] or SAY)
+end
+
+local function chat(addon, text, r, g, b, _, _, _, _, _, icon)
+ if icon then text = "|T"..icon..":15|t"..text end
+ DEFAULT_CHAT_FRAME:AddMessage(text, r, g, b)
+end
+
+local function uierror(addon, text, r, g, b, _, _, _, _, _, icon)
+ if icon then text = "|T"..icon..":20:20:-5|t"..text end
+ UIErrorsFrame:AddMessage(text, r, g, b, 1.0)
+end
+
+local rw
+do
+ local white = {r = 1, g = 1, b = 1}
+ function rw(addon, text, r, g, b, _, _, _, _, _, icon)
+ if r or g or b then
+ local c = "|cff" .. string.format("%02x%02x%02x", (r or 0) * 255, (g or 0) * 255, (b or 0) * 255)
+ text = c .. text .. "|r"
+ end
+ if icon then text = "|T"..icon..":20:20:-5|t"..text end
+ RaidNotice_AddMessage(RaidWarningFrame, text, white)
+ end
+end
+
+local function noop() --[[ noop! ]] end
+
+local handlerPriority = { "Parrot", "SCT", "MikSBT", "BCF" }
+-- Thanks to ckk for these
+local customHandlersEnabled = {
+ Parrot = function()
+ if not _G.Parrot then return end
+ return _G.Parrot.IsEnabled and _G.Parrot:IsEnabled() or _G.Parrot:IsActive()
+ end,
+ SCT = function()
+ return _G.SCT and _G.SCT:IsEnabled()
+ end,
+ BCF = function()
+ return bcfDB and bcfDB["enable"]
+ end,
+}
+
+-- Default to version 5 or higher now
+local msbtVersion = tonumber(string.match(GetAddOnMetadata("MikScrollingBattleText", "Version") or "","^%d+\.%d+")) or 5
+local isMSBTFive = math.floor(msbtVersion) > 4 and true or nil
+if isMSBTFive then
+ customHandlersEnabled.MikSBT = function()
+ return _G.MikSBT and not _G.MikSBT.IsModDisabled()
+ end
+else
+ customHandlersEnabled.MikSBT = function()
+ return _G.MikSBT and _G.MSBTProfiles and _G.MSBTProfiles.GetSavedVariables() and not MSBTProfiles.GetSavedVariables().UserDisabled
+ end
+end
+
+local currentHandler = nil
+local function getPrioritizedSink()
+ if currentHandler then
+ local check = customHandlersEnabled[currentHandler]
+ if check and check() then
+ return sink.handlers[currentHandler]
+ end
+ end
+ for i, v in ipairs(handlerPriority) do
+ local check = customHandlersEnabled[v]
+ if check and check() then
+ currentHandler = v
+ return sink.handlers[v]
+ end
+ end
+ if SHOW_COMBAT_TEXT and tostring(SHOW_COMBAT_TEXT) ~= "0" then
+ return blizzard
+ end
+ return chat
+end
+
+local function pour(addon, text, r, g, b, ...)
+ local func = sink.override and sink.handlers[sink.override] or nil
+ if not func and sink.storageForAddon[addon] and sink.storageForAddon[addon].sink20OutputSink then
+ local h = sink.storageForAddon[addon].sink20OutputSink
+ func = sink.handlers[h]
+ -- If this sink is not available now, find one manually.
+ if customHandlersEnabled[h] and not customHandlersEnabled[h]() then
+ func = nil
+ end
+ end
+ if not func then
+ func = getPrioritizedSink()
+ end
+ if not func then func = chat end
+ func(addon, text, r or 1, g or 1, b or 1, ...)
+end
+
+function sink:Pour(textOrAddon, ...)
+ local t = type(textOrAddon)
+ if t == "string" then
+ pour(self, textOrAddon, ...)
+ elseif t == "table" then
+ pour(textOrAddon, ...)
+ else
+ error("Invalid argument 2 to :Pour, must be either a string or a table.")
+ end
+end
+
+local sinks
+do
+ -- Maybe we want to hide them instead of disable
+ local function shouldDisableSCT()
+ return not _G.SCT
+ end
+ local function shouldDisableMSBT()
+ return not _G.MikSBT
+ end
+ local function shouldDisableBCF()
+ return not ( bcfDB and bcfDB["enable"] )
+ end
+ local function shouldDisableParrot()
+ return not _G.Parrot
+ end
+ local function shouldDisableFCT()
+ return not SHOW_COMBAT_TEXT or tostring(SHOW_COMBAT_TEXT) == "0"
+ end
+
+ local channelAreas = { SAY, YELL, PARTY, GUILD_CHAT, OFFICER_CHAT, RAID, RAID_WARNING, BATTLEGROUND }
+ local sctFrames = {"Incoming", "Outgoing", "Messages"}
+ local msbtFrames = nil
+ local function getScrollAreasForAddon(addon)
+ if type(addon) ~= "string" then return nil end
+ if addon == "Parrot" then
+ if Parrot.GetScrollAreasChoices then
+ return Parrot:GetScrollAreasChoices()
+ else
+ return Parrot:GetScrollAreasValidate()
+ end
+ elseif addon == "MikSBT" then
+ if isMSBTFive then
+ if not msbtFrames then
+ msbtFrames = {}
+ for key, name in MikSBT.IterateScrollAreas() do
+ table.insert(msbtFrames, name)
+ end
+ end
+ return msbtFrames
+ else
+ return MikSBT.GetScrollAreaList()
+ end
+ elseif addon == "BCF" then
+ if bcfDB then
+ local bcfAreas = { }
+ for i = 1, #bcfDB["scrollAreas"] do
+ table.insert(bcfAreas, bcfDB["scrollAreas"][i]["name"])
+ end
+ return bcfAreas
+ end
+ elseif addon == "SCT" then
+ return sctFrames
+ elseif addon == "Channel" then
+ return channelAreas
+ elseif sink.registeredScrollAreaFunctions[addon] then
+ return sink.registeredScrollAreaFunctions[addon]()
+ end
+ return nil
+ end
+
+ local emptyTable, args, options = {}, {}, {}
+ sinks = {
+ Default = {L_DEFAULT, L_DEFAULT_DESC},
+ SCT = {L_SCT, nil, shouldDisableSCT},
+ MikSBT = {L_MSBT, nil, shouldDisableMSBT},
+ BCF = {L_BCF, nil, shouldDisableBCF},
+ Parrot = {L_PARROT, nil, shouldDisableParrot},
+ Blizzard = {L_BLIZZARD, nil, shouldDisableFCT},
+ RaidWarning = {L_RW},
+ ChatFrame = {L_CHAT},
+ Channel = {L_CHANNEL},
+ UIErrorsFrame = {L_UIERROR},
+ None = {L_NONE, L_NONE_DESC}
+ }
+
+ local function getAce2SinkOptions(key, opts)
+ local name, desc, hidden = unpack(opts)
+ args["Ace2"][key] = {
+ type = "toggle",
+ name = name,
+ desc = desc or L_ROUTE:format(name),
+ isRadio = true,
+ hidden = hidden
+ }
+ end
+
+ function sink.GetSinkAce2OptionsDataTable(addon)
+ options["Ace2"][addon] = options["Ace2"][addon] or {
+ output = {
+ type = "group",
+ name = L_OUTPUT,
+ desc = L_OUTPUT_DESC,
+ pass = true,
+ get = function(key)
+ if not sink.storageForAddon[addon] then
+ return "Default"
+ end
+ if tostring(key) == "nil" then
+ -- Means AceConsole wants to list the output option,
+ -- so we should show which sink is currently used.
+ return sink.storageForAddon[addon].sink20OutputSink or L_DEFAULT
+ end
+ if key == "ScrollArea" then
+ return sink.storageForAddon[addon].sink20ScrollArea
+ elseif key == "Sticky" then
+ return sink.storageForAddon[addon].sink20Sticky
+ else
+ if sink.storageForAddon[addon].sink20OutputSink == key then
+ local sa = getScrollAreasForAddon(key)
+ options["Ace2"][addon].output.args.ScrollArea.validate = sa or emptyTable
+ options["Ace2"][addon].output.args.ScrollArea.disabled = not sa
+ options["Ace2"][addon].output.args.Sticky.disabled = not sink.stickyAddons[key]
+ end
+ return sink.storageForAddon[addon].sink20OutputSink and sink.storageForAddon[addon].sink20OutputSink == key or nil
+ end
+ end,
+ set = function(key, value)
+ if not sink.storageForAddon[addon] then return end
+ if key == "ScrollArea" then
+ sink.storageForAddon[addon].sink20ScrollArea = value
+ elseif key == "Sticky" then
+ sink.storageForAddon[addon].sink20Sticky = value
+ elseif value then
+ local sa = getScrollAreasForAddon(key)
+ options["Ace2"][addon].output.args.ScrollArea.validate = sa or emptyTable
+ options["Ace2"][addon].output.args.ScrollArea.disabled = not sa
+ options["Ace2"][addon].output.args.Sticky.disabled = not sink.stickyAddons[key]
+ sink.storageForAddon[addon].sink20OutputSink = key
+ end
+ end,
+ args = args["Ace2"],
+ disabled = function()
+ return (type(addon.IsActive) == "function" and not addon:IsActive()) or nil
+ end
+ }
+ }
+ return options["Ace2"][addon]
+ end
+
+ -- Ace3 options data table format
+ local function getAce3SinkOptions(key, opts)
+ local name, desc, hidden = unpack(opts)
+ args["Ace3"][key] = {
+ type = "toggle",
+ name = name,
+ desc = desc or L_ROUTE:format(name),
+ hidden = hidden
+ }
+ end
+
+ function sink.GetSinkAce3OptionsDataTable(addon)
+ if not options["Ace3"][addon] then
+ options["Ace3"][addon] = {
+ type = "group",
+ name = L_OUTPUT,
+ desc = L_OUTPUT_DESC,
+ args = args["Ace3"],
+ get = function(info)
+ local key = info[#info]
+ if not sink.storageForAddon[addon] then
+ return "Default"
+ end
+ if tostring(key) == "nil" then
+ -- Means AceConsole wants to list the output option,
+ -- so we should show which sink is currently used.
+ return sink.storageForAddon[addon].sink20OutputSink or L_DEFAULT
+ end
+ if key == "ScrollArea" then
+ return sink.storageForAddon[addon].sink20ScrollArea
+ elseif key == "Sticky" then
+ return sink.storageForAddon[addon].sink20Sticky
+ else
+ if sink.storageForAddon[addon].sink20OutputSink == key then
+ local sa = getScrollAreasForAddon(key)
+ if sa then
+ for k,v in ipairs(sa) do
+ sa[k] = nil
+ sa[v] = v
+ end
+ end
+ options["Ace3"][addon].args.ScrollArea.values = sa or emptyTable
+ options["Ace3"][addon].args.ScrollArea.disabled = not sa
+ options["Ace3"][addon].args.Sticky.disabled = not sink.stickyAddons[key]
+ end
+ return sink.storageForAddon[addon].sink20OutputSink and sink.storageForAddon[addon].sink20OutputSink == key or nil
+ end
+ end,
+ set = function(info, v)
+ local key = info[#info]
+ if not sink.storageForAddon[addon] then return end
+ if key == "ScrollArea" then
+ sink.storageForAddon[addon].sink20ScrollArea = v
+ elseif key == "Sticky" then
+ sink.storageForAddon[addon].sink20Sticky = v
+ elseif v then
+ local sa = getScrollAreasForAddon(key)
+ if sa then
+ for k,v in ipairs(sa) do
+ sa[k] = nil
+ sa[v] = v
+ end
+ end
+ options["Ace3"][addon].args.ScrollArea.values = sa or emptyTable
+ options["Ace3"][addon].args.ScrollArea.disabled = not sa
+ options["Ace3"][addon].args.Sticky.disabled = not sink.stickyAddons[key]
+ sink.storageForAddon[addon].sink20OutputSink = key
+ end
+ end,
+ disabled = function()
+ return (type(addon.IsEnabled) == "function" and not addon:IsEnabled()) or nil
+ end,
+ }
+ end
+ return options["Ace3"][addon]
+ end
+
+ local sinkOptionGenerators = {
+ ["Ace2"] = getAce2SinkOptions,
+ ["Ace3"] = getAce3SinkOptions
+ }
+ for generatorName, generator in pairs(sinkOptionGenerators) do
+ options[generatorName] = options[generatorName] or {}
+ args[generatorName] = args[generatorName] or {}
+ for name, opts in pairs(sinks) do
+ generator(name, opts)
+ end
+ end
+
+ args["Ace2"].ScrollArea = {
+ type = "text",
+ name = L_SCROLL,
+ desc = L_SCROLL_DESC,
+ validate = emptyTable,
+ order = -1,
+ disabled = true
+ }
+ args["Ace2"].Sticky = {
+ type = "toggle",
+ name = L_STICKY,
+ desc = L_STICKY_DESC,
+ validate = emptyTable,
+ order = -2,
+ disabled = true
+ }
+
+ args["Ace3"].ScrollArea = {
+ type = "select",
+ name = L_SCROLL,
+ desc = L_SCROLL_DESC,
+ values = emptyTable,
+ order = -1,
+ disabled = true
+ }
+ args["Ace3"].Sticky = {
+ type = "toggle",
+ name = L_STICKY,
+ desc = L_STICKY_DESC,
+ order = -2,
+ disabled = true
+ }
+
+ function sink:RegisterSink(shortName, name, desc, func, scrollAreaFunc, hasSticky)
+ assert(type(shortName) == "string")
+ assert(type(name) == "string")
+ assert(type(desc) == "string" or desc == nil)
+ assert(type(func) == "function" or type(func) == "string")
+ assert(type(scrollAreas) == "function" or scrollAreas == nil)
+ assert(type(hasSticky) == "boolean" or hasSticky == nil)
+
+ if sinks[shortName] or sink.handlers[shortName] then
+ error("There's already a sink by the short name %q.", shortName)
+ end
+ sinks[shortName] = {name, desc}
+ -- Save it for library upgrades.
+ if not sink.registeredSinks then sink.registeredSinks = {} end
+ sink.registeredSinks[shortName] = sinks[shortName]
+
+ if type(func) == "function" then
+ sink.handlers[shortName] = func
+ else
+ sink.handlers[shortName] = function(...)
+ self[func](self, ...)
+ end
+ end
+ if type(scrollAreaFunc) == "function" then
+ sink.registeredScrollAreaFunctions[shortName] = scrollAreaFunc
+ elseif type(scrollAreaFunc) == "string" then
+ sink.registeredScrollAreaFunctions[shortName] = function(...)
+ return self[scrollAreaFunc](self, ...)
+ end
+ end
+ sink.stickyAddons[shortName] = hasSticky and true or nil
+
+ for k, v in pairs(sinkOptionGenerators) do
+ v(shortName, sinks[shortName])
+ end
+ end
+end
+
+function sink.SetSinkStorage(addon, storage)
+ assert(type(addon) == "table")
+ assert(type(storage) == "table", "Storage must be a table")
+ sink.storageForAddon[addon] = storage
+end
+
+-- Sets a sink override for -all- addons, librarywide.
+function sink:SetSinkOverride(override)
+ assert(type(override) == "string" or override == nil)
+ if override and not sink.handlers[override] then
+ error("There's no %q sink.", override)
+ end
+ sink.override = override
+end
+
+-- Put this at the bottom, because we need the local functions to exist first.
+local handlers = {
+ Parrot = parrot,
+ SCT = sct,
+ MikSBT = msbt,
+ BCF = bcf,
+ ChatFrame = chat,
+ Channel = channel,
+ UIErrorsFrame = uierror,
+ Blizzard = blizzard,
+ RaidWarning = rw,
+ None = noop,
+}
+-- Overwrite any handler functions from the old library
+for k, v in pairs(handlers) do
+ sink.handlers[k] = v
+end
+
+-----------------------------------------------------------------------
+-- Embed handling
+
+sink.embeds = sink.embeds or {}
+
+local mixins = {
+ "Pour", "RegisterSink", "SetSinkStorage",
+ "GetSinkAce2OptionsDataTable", "GetSinkAce3OptionsDataTable"
+}
+
+function sink:Embed(target)
+ sink.embeds[target] = true
+ for _,v in pairs(mixins) do
+ target[v] = sink[v]
+ end
+ return target
+end
+
+for addon in pairs(sink.embeds) do
+ sink:Embed(addon)
+end
diff --git a/Libs/LibSink-2.0/lib.xml b/Libs/LibSink-2.0/lib.xml
new file mode 100644
index 0000000..9770813
--- /dev/null
+++ b/Libs/LibSink-2.0/lib.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/Libs/LibStub/LibStub.lua b/Libs/LibStub/LibStub.lua
new file mode 100644
index 0000000..cfc97de
--- /dev/null
+++ b/Libs/LibStub/LibStub.lua
@@ -0,0 +1,30 @@
+-- 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
diff --git a/Libs/LibStub/LibStub.toc b/Libs/LibStub/LibStub.toc
new file mode 100644
index 0000000..b43e20c
--- /dev/null
+++ b/Libs/LibStub/LibStub.toc
@@ -0,0 +1,13 @@
+## Interface: 20400
+## Title: Lib: LibStub
+## Notes: Universal Library Stub
+## Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel
+## X-Website: http://jira.wowace.com/browse/LS
+## X-Category: Library
+## X-License: Public Domain
+## X-Curse-Packaged-Version: 1.0
+## X-Curse-Project-Name: LibStub
+## X-Curse-Project-ID: libstub
+## X-Curse-Repository-ID: wow/libstub/mainline
+
+LibStub.lua
diff --git a/Localization/deDE.lua b/Localization/deDE.lua
index 79e083a..41ea6a8 100644
--- a/Localization/deDE.lua
+++ b/Localization/deDE.lua
@@ -1,10 +1,331 @@
--- THIS CONTENTS OF THIS FILE IS AUTO-GENERATED BY THE WOWACE PACKAGER
--- Please use the Localization App on WoWAce to update this
--- at http://www.wowace.com/projects/omen-threat-meter/localization/
-
--- Translation courtesy of Stan (Arcádia - EU Zirkel des Cenarius)
-local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
-local L = AceLocale:NewLocale("Omen", "deDE")
-if not L then return end
-
---@localization(locale="deDE", format="lua_additive_table", table-name="L", handle-unlocalized="comment")@
+-- THIS CONTENTS OF THIS FILE IS AUTO-GENERATED BY THE WOWACE PACKAGER
+-- Please use the Localization App on WoWAce to update this
+-- at http://www.wowace.com/projects/omen-threat-meter/localization/
+
+-- Translation courtesy of Stan (Arcádia - EU Zirkel des Cenarius)
+local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
+local L = AceLocale:NewLocale("Omen", "deDE")
+if not L then return end
+
+L["A collection of help pages"] = "Eine Sammlung von Hilfeseiten"
+L["Alpha"] = "Sichtbarkeit"
+L["Always Show Self"] = "Immer selbst anzeigen"
+L["Always show your threat bar on Omen (ignores class filter settings), showing your bar on the last row if necessary"] = "Zeige den eigenen Bedrohungsbalken (ignoriert Klassenfilter), immer in der letzten Reihe an, wenn nötig."
+L["Animate Bars"] = "Balken animieren"
+L["Attach to minimap"] = "An der Minimap anbringen"
+L["Autocollapse"] = "Automatisches Zusammenklappen"
+L["Autocollapse Options"] = "Optionen für das automatische Zusammenklappen"
+L["AUTO_SHOW/HIDE_NOTE"] = "Anmerkung: Wenn Sie Omen manuell Ein- oder Ausblenden, wird es ein- oder ausgeblendet bleiben, egal welche Einstellungen Sie in den Anzeigen/Verstecken Einstellungen vorgenommen haben, bis Sie das nächste mal die Zone wechseln, eine(r) Gruppe verlassen/beitreten oder die Anzeigen/Verstecken Einstellungen ändern."
+L["Background Color"] = "Hintergrundfarbe"
+L["Background Options"] = "Optionen für den Hintergrund"
+L["Background Texture"] = "Hintergrundtextur"
+L["Background Tile Size"] = "Größe der Hintergrundkacheln"
+L["Bar BG Color"] = "Balkenhintergrundfarbe"
+L["Bar Height"] = "Balkenhöhe"
+L["Bar Inset"] = "Balkeneinfügung"
+L["Bar Label Options"] = "Optionen für die Beschriftung der Balken"
+L["Bar Settings"] = "Optionen für die Balken"
+L["Bar Spacing"] = "Abstand der Balken"
+L["Bar Texture"] = "Balktentextur"
+L["Border Color"] = "Rahmenfarbe"
+L["Border Texture"] = "Rahmentextur"
+L["Border Thickness"] = "Dicke des Rahmens"
+L["Causes Omen to play a chosen sound effect"] = "Lässt Omen einen ausgewählen Ton abspielen"
+L["Causes the entire game world to shake momentarily. This option only works if nameplates are turned off."] = "Lässt die Spielwelt für einen kurzen Moment beben. Diese Option funktioniert nur wenn Namenschilder deaktiviert sind"
+L["Causes the entire screen to flash red momentarily"] = "Lässt den gesamten Bildschirm kurzzeitig rot aufleuchten"
+L["Center"] = "Mitte"
+L["Center Omen"] = "Omen zentrieren"
+L["|cffff0000Error:|r Omen cannot use shake warning if you have turned on nameplates at least once since logging in."] = "|cffff0000Fehler:|r Omen kann keine Wackelwarnungen benutzen wenn Namensschilder aktiviert wurden, nachdem Ihr Euch eingeloggt habt."
+L["Clamp To Screen"] = "Auf dem Bildschirm halten"
+L["Click|r to toggle the Omen window"] = "Links-Klick|r, um das Omen Fenster ein/auszublenden."
+-- L["Click Through"] = "Click Through"
+L["Collapse to show a minimum number of bars"] = "Zusammenklappen um nur eine minimale Anzahl an Balken zu zeigen"
+L["Configure"] = "Einstellungen"
+L["Configure bar settings."] = "Einstellungen für die Balken ändern"
+L["Configure title bar settings."] = "Einstellungen für die Titelleiste ändern"
+L["Controls the frame strata of the main Omen window. Default: MEDIUM"] = "Kontrolliert die Höhenstufe des Omen Hauptfensters. Standard: MEDIUM"
+L["Controls the scaling of the main Omen window."] = "Kontrolliert die Skalierung des Omen Hauptfensters"
+L["Controls the transparency of the main Omen window."] = "Kontrolliert die Sichtbarkeit des Omen Hauptfensters"
+L["Controls whether the main Omen window can be dragged offscreen"] = "Kontrolliert ob das Omen Fenster über die Bildschirmkanten hinaus verschoben werden kann"
+L["Control the font size of the labels"] = "Die Schriftgröße der Balken ändern"
+L["Control the font size of the title text"] = "Die Schriftgröße des Titeltextes ändern"
+L["DEATHKNIGHT"] = "Todesritter"
+L["Disable while tanking"] = "Beim Tanken deaktivieren"
+L["DISABLE_WHILE_TANKING_DESC"] = "Gibt keine Warnmeldungen aus wenn Verteidigungshaltung, Bärengestalt, Zorn der Gerechtigkeit oder Frost Präsenz aktiv sind."
+L["Display large numbers in Ks"] = "Zeigt hohe Zahlenwerte im K-Format (1k = 1000) an"
+L["DRUID"] = "Druide"
+L["HERO"] = "Hero"
+L["Enable Screen Flash"] = "Bildschirmblinken aktivieren"
+L["Enable Screen Shake"] = "Bildschirmwackeln aktivieren"
+L["Enable Sound"] = "Ton aktivieren"
+L["Enable Warning Message"] = "Warnmeldungen aktivieren"
+L["Fade/MI Bar Color"] = "Verblassen/Spiegelbild-Leistenfarbe"
+L["FAQ Part 1"] = "Häufig gestellte Fragen - 1"
+L["FAQ Part 2"] = "Häufig gestellte Fragen - 2"
+L["Font"] = "Schriftart"
+L["Font Color"] = "Schriftfarbe"
+L["Font Outline"] = "Schriftkontur"
+L["Font Size"] = "Schriftgröße"
+L["Frame's background color"] = "Farbe die für den Hintergrund des Fensters genutzt werden soll"
+L["Frame's border color"] = "Farbe die für den Rand des Fensters genutzt werden soll"
+L["Frame Strata"] = "Höhenstufe"
+L["Frequently Asked Questions"] = "Häufig gestellte Fragen"
+L["FuBar Options"] = "FuBar Optionen"
+L["GENERAL_FAQ"] = [=[|cffffd200Was unterscheidet Omen3 von Omen2?|r
+
+Omen3 stützt sich komplett auf die Blizzard Bedrohungs API und Bedrohungs Events. Es versucht nicht mehr Bedrohung zu berechnen oder hochzurechnen wie es Omen2 tat.
+
+Omen2 hat die sogenannte Threat-2.0 Library benutzt. Diese Library war verantwortlich für die Überwachung des Kampflogs, das Wirken von Zaubern, Buffs, Debuffs, Haltungen, Talenten und Modifikatoren auf Ausrüstungen, um die Bedrohung jedes Einzelnen zu berechnen. Bedrohung wurde auf Grund von Vermutungen und Annahmen berechnet, die durch Beobachtungen entstanden sind. Viele Fähigkeiten wie z.b Knockbacks wurden nur angenommen(als 50% Bedrohungsreduzierung) da es meist unmöglich war, sie zu verifizieren.
+
+Die Threat-2.0 Library enthielt auch ein Kommunikationssystem um Ihre Bedrohung an den Rest der Schlachtgruppe zu senden, solange dieser auch Threat-2.0 benutzen. Diese Daten wurden dann dazu genutzt einen schlachtzugsweiten Überblick der Bedrohungssituation darzustellen.
+
+Seit dem Patch 3.0.2, macht Omen keines dieser Dinge mehr und eine Thread Library ist damit nicht länger notwendig. Omen3 nutzt Blizzards neuen, eingebauten, Bedrohungsmonitor um exakte Werte für die Bedrohung jedes Mitglieds zu empfangen. Dies bedeutet, Omen muss nicht länger mit anderen Mitspielern Daten synchronisieren, den Kampflog auslesen oder schätzen. Dies resultiert in einer viel besseren Geschwindigkeit im Bezug auf Netzwerkauslastung, CPU-Zeit und RAM-Verbrauch. Die Implementierung von Bossmodulen für spezielle Ereignisse (wie z.B. das Zurücksetzen der Bedrohung bei der Landung des "Schreckens der Nacht") ist nicht länger notwendig.
+
+Ein weiterer Vorteil dieser Implementierung ist die Anzeige der Bedrohung von NPCs (z.B. die menschliche Form von Kalecgos). Allerdings gibt es auch einige Nachteile; Die Frequenz der Aktualisierungen ist viel geringer, Bedrohung kann nur noch erfasst werden wenn jemand in Ihrer Gruppe den Mob im Ziel hat und sich im direkten Kampf mit diesem Monster befindet.
+
+|cffffd200Wie werde ich die zwei vertikalen Linien in der Mitte los?|r
+
+Verankert Euer Omen. Wird Omen an der aktuellen Position verankert, kann es weder verschoben noch in der Größe verändert werden, dies verhindert auch, dass Spalten in der Größe verändert werden. Falls es wem jetzt noch nicht klar ist, die zwei vertikalen Linien dienen zum Verändern der Spaltenbreite.
+
+|cffffd200Wie lasse ich Omen3 wie Omen2 aussehen?|r
+
+Ändert sowohl die Hintergrundtextur als auch die Randtextur auf Blizzard Tooltip, ändert ebenfalls die Hintergrundfarbe auf Schwarz (indem ihr den Sichtbarkeitsbalken ganz nach unten zieht) und die Randfarbe auf blau einstellt.
+
+|cffffd200Warum wird nichts angezeigt obwohl ich ein Monster im Ziel habe, welches sich im Kampf befindet?|r
+
+Die Blizzard Threat API gibt keine Bedrohungsdaten zurück wenn Ihr nicht im direkten Kampf mit diesem Monster seid. Wir vermuten dies ist ein Versuch von Blizzard den Netzwerkverkehr zu reduzieren.
+
+|cffffd200Besteht irgendeine Möglichkeit diese Blizzard limitation zu umgehen? Da es ist mir unmöglich ist die Aggro meines Begleiters zu sehen bevor ich selber angreife, muss ich nun wieder schätzen.|r
+
+Kurz gesagt, es gibt keine Möglichkeit das zu umgehen ausser wir würden wieder für Euch schätzen (was ja ursprünglich Omen 2 gemacht hat).
+
+Das Ziel von Omen3 ist jedoch, Daten so exakt wie möglich zu liefern, wir bezwecken ja nicht durch Schätzungen Eure FPS zu mindern.
+
+Habt einfach ein wenig Vertrauen in Euren Begleiter/Tank, oder wartet schlicht ein paar Sekunden bevor ihr selber angreift und nutzt dabei sicherheitshalber einen weniger Bedrohung erzeugenden Angriff/Zauber (wie z.B. Eislanze) um die Daten angezeigt zu bekommen]=]
+L["GENERAL_FAQ2"] = [=[|cffffd200Können wir den AoE Modus wieder haben?|r
+
+Noch einmal, es ist nicht möglich ohne die Bedrohungswerte zu schätzen. Blizzards Bedrohungs API erlaubt es uns nur für Einheiten die eine Person im Schlachtzug als Ziel hat zu empfangen. Das bedeutet, wenn es 20 Mobs gibt, und nur 6 davon bei Schlachtzugsmitgliedern im Ziel sind, es keinen Weg gibt um genaue Bedrohungswerte der anderen 14 zu empfangen.
+
+Es ist auch sehr schwierig zu schätzen, gerade bei Heilung und Buffs (Bedrohung wird durch die Anzahl an Mobs geteilt mit denen Sie sich im Kampf befinden), da Mobs die sich unter Crowd Control Effekten (Schaf, Verbannung, Kopfnuss, usw) befinden, keine veränderte Bedrohungstabelle besitzen und Addons nicht zuverlässig sagen können mit vielen Mobs Sie sich im Kampf befinden. Der von Omen2 geschätze Wert war meistens nicht richtig.
+
+|cffffd200Die Tooltips einer Einheit zeigt die Bedrohung in % an die nicht den % entspricht, die Omen3 anzeigt. Warum?|r
+
+Blizzards Bedrohungsprozente skalieren zwischen 0% und 100%, so dass man immer bei 100% Aggro zieht. Omen zeigt jedoch unskalierte Rohwerte an, bei denen man bei 110% Aggro zieht, wenn man sich in Nahkampfreichweite befindet und bei 130% wenn im Fernkampfbereich.
+
+Nach allgemeinem Verständnis, wird das Hauptziel eines Mobs als Tank bezeichnet und hat 100% Bedrohung.
+
+|cffffd200Synchronisiert sich Omen3 mit, oder holt Daten aus, dem Kampflog?|r
+
+Nein. Omen3 versucht nicht Daten mit anderen Spielern abzugleichen oder aus dem Kampflog zu ziehen. Momentan ist es auch nicht geplant dies zu tun.
+
+|cffffd200Die Aktualisierungsrate der Bedrohung ist langsam...|r
+
+Omen3 aktualisiert genau so oft die Bedrohungswerte, wie Blizzard es zulässt.
+
+In der Tat aktualisiert Blizzard diese etwa einmal pro Sekunde, was sogar viel schneller ist, als Omen2 es getan hat. In Omen2, wurde Eure Bedrohung nur etwa alle 3 Sekunden an den Rest des Schlachtzuges gesendet (als Tank alle 1.5s).
+
+|cffffd200Wo kann ich Fehler melden oder Vorschläge machen?|r
+
+http://forums.wowace.com/showthread.php?t=14249
+
+|cffffd200Wer hat Omen3 geschrieben?|r
+
+Xinhuan (Blackrock US Alliance).
+
+|cffffd200Sind Spenden via Paypal möglich?|r
+
+Ja, schicken Sie sie an xinhuan AT gmail DOT com.
+]=]
+L["General Settings"] = "Allgemeine Optionen"
+L["Grow bars upwards"] = "Balken nach oben aufbauen"
+L["Heading background color"] = "Hintergrundfarbe der Überschriften"
+L["Heading BG Color"] = "Überschriftenhintergrundfarbe"
+L["Height of each bar"] = "Höhe jedes Balkens"
+L["Height of the title bar. The minimum height allowed is twice the background border thickness."] = "Höhe der Titelleisten. Die minimal erlaubte Höhe ist doppelt so dick wie der Rand des Hintergrundfensters"
+L["Help File"] = "Hilfe"
+L["Hide minimap/FuBar icon"] = "Minimap/FuBar Symbol anzeigen/verstecken"
+L["Hide Omen"] = "Omen verstecken"
+L["Hide Omen entirely if it collapses to show 0 bars"] = "Omen komplett ausblenden wenn es zusammenklappt um 0 Balken anzuzeigen"
+L["Hide Omen on 0 bars"] = "Omen ausblenden wenn keine Balken vorhanden sind"
+L["However, hide Omen if any of the following are true (higher priority than the above)."] = "Verstecke Omen wenn folgende Bedingungen zutreffen (höhere Priorität als die oberen)."
+L["HUNTER"] = "Jäger"
+L["Ignore Player Pets"] = "Spieler Begleiter ignorieren"
+L["IGNORE_PLAYER_PETS_DESC"] = [=[Veranlasst Omen gegnerische Spielerbegleiter ausser acht zu lassen wenn bestimmt wird auf welche Einheit Bedrohung angezeigt werden soll.
+
+Spielerbegleiter haben nur eine Bedrohungsliste wenn sie sich im |cffffff78Aggressiven|r oder |cffffff78Defensiven|r Modus befinden und sich somit wie normale Mobs verhalten, also das Ziel mit der größten Bedrohung angreiffen. Wird der Beleiter dazu veranlasst, eine bestimntes Ziel anzugreiffen, behält der Begleiter eine Bedrohungstabelle, aber bleibt bei dem festgelegten Ziel, welches per Definition 100% Bedrohung hat. Spielerbegleiter können abgespottet werden um sie zu zwingen Sie anzugreiffen.
+
+Spielerbegleiter im |cffffff78Passiven|r Modus haben auf jeden Fall keine Bedrohungstabelle und Spott funktioniert bei ihnen nicht. Sie greiffen das ausgewählte Ziel nur auf Befehl an und das ohne Bedrohungstabelle.
+
+Wenn ein Spielerbegleiter dazu aufgefordert wird, zu |cffffff78Folgen|r, wird die Bedrohungstabelle gelöscht und es hört auf anzugreiffen, dennoch kann es sofort wieder ein neues Ziel aufnehmen, wenn wieder in den Aggressiven/Defensiven Modus gewechselt wird.
+]=]
+L["Invert Bar/Text Colors"] = "Leisten-/Textfarben umkehren"
+L["Left"] = "Links"
+L["Lock Omen"] = "Omen verankern"
+L["Locks Omen in place and prevents it from being dragged or resized."] = "Verankert Omenan seiner momentanen Position, um zu verhindern, dass es verschoben oder die Größe geändert wird."
+L["MAGE"] = "Magier"
+-- L["Makes the Omen window non-interactive"] = "Makes the Omen window non-interactive"
+L["Max bars to show"] = "Maximale Anzahl an Balken"
+L["Max number of bars to show"] = "Anzahl der Balken die maximal angezeigt werden"
+L["'My Bar' BG Color"] = "Hintergrundfarbe für den eigenen Balken"
+L["Name"] = "Name"
+L["None"] = "Keine"
+L["*Not in Party*"] = "*Nicht in der Gruppe*"
+L["OMEN_DESC"] = "Omen ist ein leichtgewichtiges Bedrohungsmeter, dass Euch den Bedrohungsstatus an Monstern anzeigt mit denen Ihr Euch im Kampf befindet. Ihr könnt das Aussehen und Verhalten von Omen ändern und verschiedene Profile für Euere Spielfiguren festlegen."
+L["Omen Quick Menu"] = "Omen Schnellmenü"
+L["OMEN_SLASH_DESC"] = "Diese Knöpfe bewirken das selbe wie die Slashkommandos beim Befehl /omen"
+L["OMEN_WARNINGS_DESC"] = "Dieser Bereich erlaubt Euch einzustellen, wann und wie Omen Euch benachrichtigt wenn Ihr kurz davor seid Aggro zu ziehen"
+L["Open Config"] = "Optionen öffnen"
+L["Open Omen's configuration panel"] = "Omens Konfigurationsmenü öffnen"
+L["Open the configuration dialog"] = "Den Konfigurationsdialog öffnen"
+L["Outline"] = "Kontur"
+L["PALADIN"] = "Paladin"
+L["Passed %s%% of %s's threat!"] = "%s%% der Bedrohung von %s überschritten!"
+L["PET"] = "Begleiter"
+L["Pet Bar Color"] = "Begleiterleistenfarbe"
+L["Position"] = "Position"
+L["PRIEST"] = "Priester"
+L["Print a message to screen when you accumulate too much threat"] = "Zeigt eine Meldung auf dem Bildschirm wenn Ihr zu viel Bedrohung erzeugt"
+L["Profiles"] = "Profile"
+L["> Pull Aggro <"] = "> Aggro ziehen <"
+L["Pull Aggro Bar Color"] = "Farbe der 'Aggro ziehen' Leiste"
+L["Right"] = "Rechts"
+L["Right-click|r to open the options menu"] = "Rechts-Klick|r, um das Optionsfenster zu öffnen."
+L["ROGUE"] = "Schurke"
+L["Scale"] = "Skalierung"
+L["Sets how far inside the frame the threat bars will display from the 4 borders of the frame"] = "Legt fest, wie weit innerhalb des Fensters die Bedrohungsbalken von den vier Rahmenseiten des Fensters entfernt sind"
+L["SHAMAN"] = "Schamane"
+L["Short Numbers"] = "Kurze Zahlen"
+L["Show a bar for the amount of threat you will need to reach in order to pull aggro."] = "Zeigt eine Leiste mit dem Wert an Bedrohung der benötigt wird um Aggro zu ziehen"
+L["Show bars for these classes"] = "Zeige Balken für diese Klassen"
+L["Show Classes..."] = "Zeige Klassen..."
+L["SHOW_CLASSES_DESC"] = "Zeite die Omen Bedrohungsbalken für folgende Klassen. Die Klassen hier betreffen alljene Leute in Eurer Gruppe/Schlachtzug mit Ausnahme der 'Nicht in der Gruppe' Option."
+L["Show column headings"] = "Zeige die Überschriften der Spalten"
+L["Show Headings"] = "Zeige Überschriften"
+L["Show icon"] = "Symbol zeigen"
+L["Show minimap button"] = "Zeige Minimap Icon"
+L["Show Omen"] = "Zeige Omen"
+L["Show Omen when..."] = "Zeige Omen wenn..."
+L["Show Omen when any of the following are true"] = "Zeige Omen wenn irgend eins der folgenden Argumente zutrifft"
+L["Show Omen when you are alone"] = "Zeige Omen wenn ich alleine bin"
+L["Show Omen when you are in a 5-man party"] = "Zeige Omen wenn ich einer 5er Gruppe bin"
+L["Show Omen when you are in a raid"] = "Zeige Omen wenn ich in einer Schlachtzuggruppe bin"
+L["Show Omen when you have a pet out"] = "Zeige Omen wenn ich einen Begleiter habe"
+L["Show Pull Aggro Bar"] = "'Aggro ziehen' Leiste anzeigen"
+L["Show text"] = "Text zeigen"
+L["Show the Omen minimap button"] = "Zeige das Omen Minimap Icon"
+L["Show the Omen Title Bar"] = "Zeige die Titelleiste von Omen an"
+L["Show Threat %"] = "Zeige % Bedrohung"
+L["Show threat per second values"] = "Zeige Bedrohung pro Sekunde Werte"
+L["Show Threat Values"] = "Zeige Bedrohungswerte"
+L["Show Title Bar"] = "Zeige Titelleiste"
+L["Show TPS"] = "Zeige BPS"
+L["Show When..."] = "Zeige wenn..."
+L["Slash Command"] = "Slashkommando"
+L["Smoothly animate bar changes"] = "Flüssiges Animieren der Balkenänderungen"
+L["Sound to play"] = "Ton der gespielt wird"
+L["Spacing between each bar"] = "Abstand zwischen den Balken"
+L["Switch the colors so that the bar background colors and the text colors are swapped."] = "Die Farben wechseln so dass die Leistenhintergrund-Farben und die Textfarben umgekehrt werden."
+L["Tank Bar Color"] = "Farbe für den Tankbalken"
+L["Tells Omen to additionally check your 'focus' and 'focustarget' before your 'target' and 'targettarget' in that order for threat display."] = "Lässt Omen zusätzlich den Fokus und das Fokusziel vor dem Ziel, und dem Ziel des Ziels für die Bedrohungsanzeige überprüfen"
+L["Tells Omen to enter Test Mode so that you can configure Omen's display much more easily."] = "Startet den Omen Testmodus um das Einstellen der Anzeigen zu vereinfachen"
+L["Test Mode"] = "Testmodus"
+L["Test warnings"] = "Testwarnung"
+L["Texture to use for the frame's background"] = "Textur die für den Hintergrund des Fensters genutzt werden soll"
+L["Texture to use for the frame's border"] = "Textur die für den Rahmen des Fensters genutzt werden soll"
+L["The background color for all threat bars"] = "Die Hintergrundfarbe für alle Bedrohungsbalken"
+L["The background color for pets"] = "Die Hintergrundfarbe für Begleiter"
+L["The background color for players under the effects of Fade and Mirror Image (they will be at negative 4 million threat)"] = "Die Hintergrundfarbe für Spieler bei denen Verblassen oder Spiegelbild aktiv ist. (ca. -4 Millionen Bedrohung)."
+L["The background color for your Pull Aggro bar"] = "Die Hintergrundfarbe für die 'Aggro ziehen' Leiste"
+L["The background color for your tank's threat bar"] = "Die Hintergrundfarbe des Bedrohungsbalkens des Tanks"
+L["The background color for your threat bar"] = "Die Hintergrundfarbe für den eigenen Bedrohungsbalken"
+L["The color of the labels"] = "Die Farbe der Beschriftungen"
+L["The color of the title text"] = "Die Farbe die der Titeltext verwenden wird"
+L["The font that the labels will use"] = "Die Schriftart welche die Balken benutzen"
+L["The font that the title text will use"] = "Die Schriftart welche der Titeltext verwenden wird"
+L["The outline that the labels will use"] = "Die Kontur der Beschriftungen"
+L["The outline that the title text will use"] = "Die Kontur die der Titeltext verwenden wird"
+L["The size used to tile the background texture"] = "Die Größe der Kacheln die für die Hintergrundtextur benutzt wird"
+L["The texture that the bar will use"] = "Die Textur welche von den Balken genutzt wird"
+L["The thickness of the border"] = "Die Dicke des Rahmens"
+L["Thick Outline"] = "Dicke Kontur"
+L["This section controls when Omen is automatically shown or hidden."] = "Dieser Bereicht kontrolliert ob Omen automatisch aus- oder eingeblendet wird"
+L["Threat"] = "Bedrohung"
+L["Threat [%]"] = "Bedrohung [%]"
+L["Tile Background"] = "Hintergrundkacheln"
+L["Tile the background texture"] = "Textur für den Hintergrund der Kacheln anzeigen"
+L["Title Bar Background Options"] = "Titelleistenhintergrundoptionen"
+L["Title Bar Height"] = "Höhe der Titelleisten"
+L["Title Bar Settings"] = "Optionen für die Titelleiste"
+L["Title Text Options"] = "Optionen für den Titeltext"
+L["Toggle Focus"] = "Fokus an/ausschalten"
+L["Toggle Omen"] = "Omen ein/ausblenden"
+L["TPS"] = "BPS"
+L["TPS Window"] = "BPS Fenster"
+L["TPS_WINDOW_DESC"] = "Die Bedrohung pro Sekunde (BPS) Berechnung basiert auf einem, in Echtzeit gestaffelten Fenster der letzten X Sekunden"
+L["Turning this on will cause Omen to hide whenever you are in a battleground or arena."] = "Wird diese Funktion aktiviert, wird Omen jedesmal wenn Ihr Euch auf einem Schlachtfeld oder in einer Arena befindet, ausgeblendet"
+L["Turning this on will cause Omen to hide whenever you are in a city or inn."] = "Wird diese Funktion aktiviert, wird Omen jedesmal wenn Ihr Euch in einer Stadt oder einem Gasthaus befindet, ausgeblendet"
+L["Turning this on will cause Omen to hide whenever you are not in combat."] = "Wenn Sie diese Option aktivieren wird Omen ausgeblendet, wenn Ihr Euch nicht im Kampf befindet"
+L[""] = ""
+L["Use a different colored background for the tank's threat bar in Omen"] = "Benutze eine andere Hintergrundfarbe für den Bedrohungsbalken des Tanks in Omen"
+L["Use a different colored background for your threat bar in Omen"] = "Eine andere Hintergrundfarbe für den eigenen Bedrohungsbalken in Omen verwenden"
+L["Use Auto Show/Hide"] = "Automatisches Anzeigen/Verstecken verwenden"
+L["Use !ClassColors"] = "Klassenfarben verwenden"
+L["Use Class Colors"] = "Klassenfarben benutzen"
+L["Use !ClassColors addon for class colors for the background color of threat bars"] = "Verwende das !ClassColors Addon für Klassenfarben bei den Bedrohungsbalken."
+L["Use Focus Target"] = "Fokusziel verwenden"
+L["Use 'My Bar' color"] = "Farbe des eigenen Balkens verwenden"
+L["Use Same Background"] = "Selben Hintergrund verwenden"
+L["Use standard class colors for the background color of threat bars"] = "Benutze die Standard Klassenfarben für den Hintergrund der Bedrohungsbalken"
+L["Use Tank Bar color"] = "Farbe des Tankbalkens verwenden"
+L["Use the same background settings for the title bar as the main window's background"] = "Die selben Hintergrundeinstellungen für die Titelleiste wie für das Hauptfenster verwenden"
+L["WARLOCK"] = "Hexer"
+L["Warning Settings"] = "Optionen für die Warnungen"
+L["Warning Threshold %"] = "Warnschwelle %"
+L["Warrior"] = "Krieger"
+L["WARRIOR"] = "Krieger"
+L["WARRIOR_FAQ"] = [=[Die folgenden Daten stammen von |cffffd200http://www.tankspot.com/forums/f200/39775-wow-3-0-threat-values.html|r am zweiten Okt. 2008 (Danke an Satrina!). Die Zahlen sind für Level 80.
+
+|cffffd200Modifiers|r
+Battle Stance ________ x 80
+Berserker Stance _____ x 80
+Tactical Mastery _____ x 121/142/163
+Defensive Stance _____ x 207.35
+
+Note that in our original threat estimations (that we use now in WoW 2.0), we equated 1 damage to 1 threat, and used 1.495 to represent the stance+defiance multiplier. We see Note that in our original threat estimations (that we use now in WoW 2.0), we equated 1 damage to 1 threat, and used 1.495 to represent the stance+defiance multiplier. We see that Blizzard's method is to use the multiplier without decimals, so in 2.x it would've been x149 (maybe x149.5); it is x207 (maybe 207.3) in 3.0. I expect that this is to allow the transport of integer values instead of decimal values across the Internet for efficiency. It appears that threat values are multiplied by 207.35 at the server, then rounded.
+
+If you still want to use the 1 damage = 1 threat method, the stance modifiers are 0.8 and 2.0735, etc.
+
+|cffffd200Threat Values (stance modifiers apply unless otherwise noted):|r
+Battle Shout _________ 78 (split)
+Cleave _______________ damage + 225 (split)
+Commanding Shout _____ 80 (split)
+Concussion Blow ______ damage only
+Damage Shield ________ damage only
+Demoralising Shout ___ 63 (split)
+Devastate ____________ damage + 5% of AP *** Needs re-checking for 8982 **
+Dodge/Parry/Block_____ 1 (in defensive stance with Improved Defensive Stance only)
+Heroic Strike ________ damage + 259
+Heroic Throw _________ 1.50 x damage
+Rage Gain ____________ 5 (stance modifier is not applied)
+Rend _________________ damage only
+Revenge ______________ damage + 121
+Shield Bash __________ 36
+Shield Slam __________ damage + 770
+Shockwave ____________ damage only
+Slam _________________ damage + 140
+Spell Reflect ________ damage only (only for spells aimed at you)
+Social Aggro _________ 0
+Sunder Armour ________ 345 + 5%AP
+Thunder Clap _________ 1.85 x damage
+Vigilance ____________ 10% of target's generated threat (stance modifier is not applied)
+
+You do not gain threat for reflecting spells targetted at allies with Improved Spell Reflect. When you reflect a spell for an ally, your ally gains the threat for the damage dealt by the reflected spell.
+]=]
+L["You are alone"] = "Ich alleine bin"
+L["You are in a battleground"] = "Ich in einem Schlachtfeld bin"
+L["You are in a party"] = "Ich in einer Gruppe bin"
+L["You are in a raid"] = "Ich in einem Schlachtzug bin"
+L["You are not in combat"] = "Ich mich nicht im Kampf befinde"
+L["You are resting"] = "Ich mich ausruhe"
+L["You have a pet"] = "Ich einen Begleiter habe"
+
diff --git a/Localization/enUS.lua b/Localization/enUS.lua
index 7a6dcfe..f2f1913 100644
--- a/Localization/enUS.lua
+++ b/Localization/enUS.lua
@@ -1,352 +1,353 @@
--- English localization file for enUS and enGB.
-local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
-local L = AceLocale:NewLocale("Omen", "enUS", true)
-if not L then return end
-
--- Main Omen window
-L[""] = true
-L["Omen Quick Menu"] = true
-L["Use Focus Target"] = true
-L["Test Mode"] = true
-L["Open Config"] = true
-L["Open Omen's configuration panel"] = true
-L["Hide Omen"] = true
-L["Name"] = true
-L["Threat [%]"] = true
-L["Threat"] = true
-L["TPS"] = true
-L["Toggle Focus"] = true
-L["> Pull Aggro <"] = true
-
--- Warnings
-L["|cffff0000Error:|r Omen cannot use shake warning if you have turned on nameplates at least once since logging in."] = true
-L["Passed %s%% of %s's threat!"] = true
-
--- Config module titles
-L["General Settings"] = true
-L["Profiles"] = true
-L["Slash Command"] = true
-
--- Config strings, general settings section
-L["OMEN_DESC"] = "Omen is a lightweight threat meter that shows you the threat of mobs you are engaged in combat with. You can change how Omen looks and behaves, and configure different profiles for every of your characters."
-L["Alpha"] = true
-L["Controls the transparency of the main Omen window."] = true
-L["Scale"] = true
-L["Controls the scaling of the main Omen window."] = true
-L["Frame Strata"] = true
-L["Controls the frame strata of the main Omen window. Default: MEDIUM"] = true
-L["Clamp To Screen"] = true
-L["Controls whether the main Omen window can be dragged offscreen"] = true
-L["Tells Omen to additionally check your 'focus' and 'focustarget' before your 'target' and 'targettarget' in that order for threat display."] = true
-L["Tells Omen to enter Test Mode so that you can configure Omen's display much more easily."] = true
-L["Collapse to show a minimum number of bars"] = true
-L["Lock Omen"] = true
-L["Locks Omen in place and prevents it from being dragged or resized."] = true
-L["Show minimap button"] = true
-L["Show the Omen minimap button"] = true
-L["Ignore Player Pets"] = true
-L["IGNORE_PLAYER_PETS_DESC"] = [[
-Tells Omen to skip enemy player pets when determining which unit to display threat data on.
-
-Player pets maintain a threat table when in |cffffff78Aggressive|r or |cffffff78Defensive|r mode and behave just like normal mobs, attacking the target with the highest threat. If the pet is instructed to attack a specific target, the pet still maintains the threat table, but sticks on the assigned target which by definition has 100% threat. Player pets can be taunted to force them to attack you.
-
-However, player pets on |cffffff78Passive|r mode do not have a threat table, and taunt does not work on them. They only attack their assigned target when instructed and do so without any threat table.
-
-When a player pet is instructed to |cffffff78Follow|r, the pet's threat table is wiped immediately and stops attacking, although it may immediately reacquire a target based on its Aggressive/Defensive mode.
-]]
-L["Click Through"] = true
-L["Makes the Omen window non-interactive"] = true
-L["Autocollapse"] = true
-L["Autocollapse Options"] = true
-L["Grow bars upwards"] = true
-L["Hide Omen on 0 bars"] = true
-L["Hide Omen entirely if it collapses to show 0 bars"] = true
-L["Max bars to show"] = true
-L["Max number of bars to show"] = true
-L["Background Options"] = true
-L["Background Texture"] = true
-L["Texture to use for the frame's background"] = true
-L["Border Texture"] = true
-L["Texture to use for the frame's border"] = true
-L["Background Color"] = true
-L["Frame's background color"] = true
-L["Border Color"] = true
-L["Frame's border color"] = true
-L["Tile Background"] = true
-L["Tile the background texture"] = true
-L["Background Tile Size"] = true
-L["The size used to tile the background texture"] = true
-L["Border Thickness"] = true
-L["The thickness of the border"] = true
-L["Bar Inset"] = true
-L["Sets how far inside the frame the threat bars will display from the 4 borders of the frame"] = true
-
--- Config strings, title bar section
-L["Title Bar Settings"] = true
-L["Configure title bar settings."] = true
-L["Show Title Bar"] = true
-L["Show the Omen Title Bar"] = true
-L["Title Bar Height"] = true
-L["Height of the title bar. The minimum height allowed is twice the background border thickness."] = true
-L["Title Text Options"] = true
-L["The font that the title text will use"] = true
-L["The outline that the title text will use"] = true
-L["The color of the title text"] = true
-L["Control the font size of the title text"] = true
-L["Use Same Background"] = true
-L["Use the same background settings for the title bar as the main window's background"] = true
-L["Title Bar Background Options"] = true
-
--- Config strings, show when... section
-L["Show When..."] = true
-L["Show Omen when..."] = true
-L["This section controls when Omen is automatically shown or hidden."] = true
-L["Use Auto Show/Hide"] = true
-L["Show Omen when any of the following are true"] = true
-L["You have a pet"] = true
-L["Show Omen when you have a pet out"] = true
-L["You are alone"] = true
-L["Show Omen when you are alone"] = true
-L["You are in a party"] = true
-L["Show Omen when you are in a 5-man party"] = true
-L["You are in a raid"] = true
-L["Show Omen when you are in a raid"] = true
-L["However, hide Omen if any of the following are true (higher priority than the above)."] = true
-L["You are resting"] = true
-L["Turning this on will cause Omen to hide whenever you are in a city or inn."] = true
-L["You are in a battleground"] = true
-L["Turning this on will cause Omen to hide whenever you are in a battleground or arena."] = true
-L["You are not in combat"] = true
-L["Turning this on will cause Omen to hide whenever you are not in combat."] = true
-L["AUTO_SHOW/HIDE_NOTE"] = "Note: If you manually toggle Omen to show or hide, it will remain shown or hidden regardless of Auto Show/Hide settings until you next zone, join or leave a party or raid, or change any Auto Show/Hide settings."
-
--- Config strings, show classes... section
-L["Show Classes..."] = true
-L["SHOW_CLASSES_DESC"] = "Show Omen threat bars for the following classes. The classes here refer to those people in your party/raid only with the exception of the 'Not In Party' option."
-L["Show bars for these classes"] = true
-L["DEATHKNIGHT"] = "Death Knight"
-L["DRUID"] = "Druid"
-L["HUNTER"] = "Hunter"
-L["MAGE"] = "Mage"
-L["PALADIN"] = "Paladin"
-L["PET"] = "Pet"
-L["PRIEST"] = "Priest"
-L["ROGUE"] = "Rogue"
-L["SHAMAN"] = "Shaman"
-L["WARLOCK"] = "Warlock"
-L["WARRIOR"] = "Warrior"
-L["*Not in Party*"] = true
-
--- Config strings, bar settings section
-L["Bar Settings"] = true
-L["Configure bar settings."] = true
-L["Animate Bars"] = true
-L["Smoothly animate bar changes"] = true
-L["Short Numbers"] = true
-L["Display large numbers in Ks"] = true
-L["Bar Texture"] = true
-L["The texture that the bar will use"] = true
-L["Bar Height"] = true
-L["Height of each bar"] = true
-L["Bar Spacing"] = true
-L["Spacing between each bar"] = true
-L["Show TPS"] = true
-L["Show threat per second values"] = true
-L["TPS Window"] = true
-L["TPS_WINDOW_DESC"] = "The threat per second calculation is based on a real time sliding window of the last X seconds."
-L["Show Threat Values"] = true
-L["Show Threat %"] = true
-L["Show Headings"] = true
-L["Show column headings"] = true
-L["Heading BG Color"] = true
-L["Heading background color"] = true
-L["Use 'My Bar' color"] = true
-L["Use a different colored background for your threat bar in Omen"] = true
-L["'My Bar' BG Color"] = true
-L["The background color for your threat bar"] = true
-L["Use Tank Bar color"] = true
-L["Use a different colored background for the tank's threat bar in Omen"] = true
-L["Tank Bar Color"] = true
-L["The background color for your tank's threat bar"] = true
-L["Show Pull Aggro Bar"] = true
-L["Show a bar for the amount of threat you will need to reach in order to pull aggro."] = true
-L["Pull Aggro Bar Color"] = true
-L["The background color for your Pull Aggro bar"] = true
-L["Use Class Colors"] = true
-L["Use standard class colors for the background color of threat bars"] = true
-L["Pet Bar Color"] = true
-L["The background color for pets"] = true
-L["Use !ClassColors"] = true
-L["Use !ClassColors addon for class colors for the background color of threat bars"] = true
-L["Fade/MI Bar Color"] = true
-L["The background color for players under the effects of Fade and Mirror Image (they will be at negative 4 million threat)"] = true
-L["Bar BG Color"] = true
-L["The background color for all threat bars"] = true
-L["Always Show Self"] = true
-L["Always show your threat bar on Omen (ignores class filter settings), showing your bar on the last row if necessary"] = true
-L["Invert Bar/Text Colors"] = true
-L["Switch the colors so that the bar background colors and the text colors are swapped."] = true
-L["Bar Label Options"] = true
-L["Font"] = true
-L["The font that the labels will use"] = true
-L["Font Size"] = true
-L["Control the font size of the labels"] = true
-L["Font Color"] = true
-L["The color of the labels"] = true
-L["Font Outline"] = true
-L["The outline that the labels will use"] = true
-L["None"] = true
-L["Outline"] = true
-L["Thick Outline"] = true
-
--- Config strings, slash command section
-L["OMEN_SLASH_DESC"] = "These buttons execute the same functions as the ones in the slash command /omen"
-L["Toggle Omen"] = true
-L["Center Omen"] = true
-L["Configure"] = true
-L["Open the configuration dialog"] = true
-L["Show Omen"] = true
-
--- Config strings, warning settings section
-L["Warning Settings"] = true
-L["OMEN_WARNINGS_DESC"] = "This section allows you to customize when and how Omen notifies you if you are about to pull aggro."
-L["Enable Sound"] = true
-L["Causes Omen to play a chosen sound effect"] = true
-L["Enable Screen Flash"] = true
-L["Causes the entire screen to flash red momentarily"] = true
-L["Enable Screen Shake"] = true
-L["Causes the entire game world to shake momentarily. This option only works if nameplates are turned off."] = true
-L["Enable Warning Message"] = true
-L["Print a message to screen when you accumulate too much threat"] = true
-L["Warning Threshold %"] = true
-L["Sound to play"] = true
-L["Disable while tanking"] = true
-L["DISABLE_WHILE_TANKING_DESC"] = "Do not give out any warnings if Defensive Stance, Bear Form, Righteous Fury or Frost Presence is active."
-L["Test warnings"] = true
-
--- Config strings, for Fubar
-L["Click|r to toggle the Omen window"] = true
-L["Right-click|r to open the options menu"] = true
-L["FuBar Options"] = true
-L["Attach to minimap"] = true
-L["Hide minimap/FuBar icon"] = true
-L["Show icon"] = true
-L["Show text"] = true
-L["Position"] = true
-L["Left"] = true
-L["Center"] = true
-L["Right"] = true
-
--- FAQ
-L["Help File"] = true
-L["A collection of help pages"] = true
-L["FAQ Part 1"] = true
-L["FAQ Part 2"] = true
-L["Frequently Asked Questions"] = true
-L["Warrior"] = true
-
-L["GENERAL_FAQ"] = [[
-|cffffd200How is Omen3 different from Omen2?|r
-
-Omen3 relies completely on the Blizzard threat API and threat events. It does not attempt to calculate or extrapolate threat unlike Omen2.
-
-Omen2 used what we called the Threat-2.0 library. This library was responsible for monitoring the combat log, spellcasting, buffs, debuffs, stances, talents and gear modifiers for calculating each individuals threat. Threat was calculated based on what was known or approximated from observed behaviors. Many abilities such as knockbacks were just assumed (to be a 50% threat reduction) as they were mostly impossible to confirm.
-
-The Threat-2.0 library also included addon communication to broadcast your threat to the rest of the raid as long as they were also using Threat-2.0. This data was then used to provide a raid wide display of threat information.
-
-Since patch 3.0.2, Omen no longer does any of these things and the need for a threat library is no longer necessary. Omen3 uses Blizzard's new in-built threat monitor to obtain exact values of every members threat. This means Omen3 has no need for synchronisation of data, combat log parsing or guessing, resulting in a significant increase in performance with regards to network traffic, CPU time and memory used. The implementation of boss modules for specific threat events (such as Nightbane wiping threat on landing) are also no longer necessary.
-
-Further benefits of this new implementation include the addition of NPC threat on a mob (eg, Human Kalecgos). However, there are some drawbacks; frequency of updates are much slower, threat details cannot be obtained unless somebody in your party/raid are targetting the mob and it is also not possible to obtain threat from a mob you are not in direct combat with.
-
-|cffffd200How do I get rid of the 2 vertical gray lines down the middle?|r
-
-Lock your Omen. Locking Omen will prevent it from being moved or resized, as well as prevent the columns from being resized. If you haven't realized it, the 2 vertical gray lines are column resizing handles.
-
-|cffffd200How do I make Omen3 look like Omen2?|r
-
-Change the both the Background Texture and Border Texture to Blizzard Tooltip, change the Background Color to black (by dragging the luminance bar to the bottom), and the Border Color to blue.
-
-|cffffd200Why does no threat data show on a mob when I target it even though it is in combat?|r
-
-The Blizzard threat API does not return threat data on any mob you are not in direct combat with. We suspect this is an effort on Blizzard's part to save network traffic.
-
-|cffffd200Is there ANY way around this Blizzard limitation? Not being able to see my pet's threat before I attack has set me back to guessing.|r
-
-There is no way around this limitation short of us doing the guessing for you (which is exactly how Omen2 did it).
-
-The goal of Omen3 is to provide accurate threat data, we no longer intend to guess for you and in the process lower your FPS. Have some confidence in your pet/tank, or just wait 2 seconds before attacking and use a low damage spell such as Ice Lance so that you can get initial threat readings.
-]]
-L["GENERAL_FAQ2"] = [[
-|cffffd200Can we get AoE mode back?|r
-
-Again, this is not really possible without guessing threat values. Blizzard's threat API only allows us to query for threat data on units that somebody in the raid is targetting. This means that if there are 20 mobs and only 6 of them are targetted by the raid, there is no way to obtain accurate threat data on the other 14.
-
-This is also extremely complicated to guess particularly for healing and buffing (threat gets divided by the number of mobs you are in combat with) because mobs that are under crowd control effects (sheep, banish, sap, etc) do not have their threat table modified and addons cannot reliably tell how many mobs you are in combat with. Omen2's guess was almost always wrong.
-
-|cffffd200The tooltips on unit mouseover shows a threat % that does not match the threat % reported by Omen3. Why?|r
-
-Blizzard's threat percentage is scaled to between 0% and 100%, so that you will always pull aggro at 100%. Omen reports the raw unscaled values which has pulling aggro percentages at 110% while in melee range and 130% otherwise.
-
-By universal agreement, the primary target of a mob is called the tank and is defined to be at 100% threat.
-
-|cffffd200Does Omen3 sync or parse the combat log?|r
-
-No. Omen3 does not attempt to sync or parse the combat log. Currently there are no intentions to do so.
-
-|cffffd200The threat updates are slow...|r
-
-Omen3 updates the threat values you see as often as Blizzard updates the threat values to us.
-
-In fact, Blizzard updates them about once per second, which is much faster than what Omen2 used to sync updates. In Omen2, you only transmitted your threat to the rest of the raid once every 3 seconds (or 1.5s if you were a tank).
-
-|cffffd200Where can I report bugs or give suggestions?|r
-
-http://forums.wowace.com/showthread.php?t=14249
-
-|cffffd200Who wrote Omen3?|r
-
-Xinhuan (Blackrock US Alliance) did.
-
-|cffffd200Do you accept Paypal donations?|r
-
-Yes, send to xinhuan AT gmail DOT com.
-]]
-L["WARRIOR_FAQ"] = [[The following data is obtained from |cffffd200http://www.tankspot.com/forums/f200/39775-wow-3-0-threat-values.html|r on 2nd Oct 2008 (credits to Satrina). The numbers are for a level 80.
-
-|cffffd200Modifiers|r
-Battle Stance ________ x 80
-Berserker Stance _____ x 80
-Tactical Mastery _____ x 121/142/163
-Defensive Stance _____ x 207.35
-
-Note that in our original threat estimations (that we use now in WoW 2.0), we equated 1 damage to 1 threat, and used 1.495 to represent the stance+defiance multiplier. We see that Blizzard's method is to use the multiplier without decimals, so in 2.x it would've been x149 (maybe x149.5); it is x207 (maybe 207.3) in 3.0. I expect that this is to allow the transport of integer values instead of decimal values across the Internet for efficiency. It appears that threat values are multiplied by 207.35 at the server, then rounded.
-
-If you still want to use the 1 damage = 1 threat method, the stance modifiers are 0.8 and 2.0735, etc.
-
-|cffffd200Threat Values (stance modifiers apply unless otherwise noted):|r
-Battle Shout _________ 78 (split)
-Cleave _______________ damage + 225 (split)
-Commanding Shout _____ 80 (split)
-Concussion Blow ______ damage only
-Damage Shield ________ damage only
-Demoralising Shout ___ 63 (split)
-Devastate ____________ damage + 5% of AP *** Needs re-checking for 8982 **
-Dodge/Parry/Block_____ 1 (in defensive stance with Improved Defensive Stance only)
-Heroic Strike ________ damage + 259
-Heroic Throw _________ 1.50 x damage
-Rage Gain ____________ 5 (stance modifier is not applied)
-Rend _________________ damage only
-Revenge ______________ damage + 121
-Shield Bash __________ 36
-Shield Slam __________ damage + 770
-Shockwave ____________ damage only
-Slam _________________ damage + 140
-Spell Reflect ________ damage only (only for spells aimed at you)
-Social Aggro _________ 0
-Sunder Armour ________ 345 + 5%AP
-Thunder Clap _________ 1.85 x damage
-Vigilance ____________ 10% of target's generated threat (stance modifier is not applied)
-
-You do not gain threat for reflecting spells targetted at allies with Improved Spell Reflect. When you reflect a spell for an ally, your ally gains the threat for the damage dealt by the reflected spell.
-]]
-
+-- English localization file for enUS and enGB.
+local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
+local L = AceLocale:NewLocale("Omen", "enUS", true)
+if not L then return end
+
+-- Main Omen window
+L[""] = true
+L["Omen Quick Menu"] = true
+L["Use Focus Target"] = true
+L["Test Mode"] = true
+L["Open Config"] = true
+L["Open Omen's configuration panel"] = true
+L["Hide Omen"] = true
+L["Name"] = true
+L["Threat [%]"] = true
+L["Threat"] = true
+L["TPS"] = true
+L["Toggle Focus"] = true
+L["> Pull Aggro <"] = true
+
+-- Warnings
+L["|cffff0000Error:|r Omen cannot use shake warning if you have turned on nameplates at least once since logging in."] = true
+L["Passed %s%% of %s's threat!"] = true
+
+-- Config module titles
+L["General Settings"] = true
+L["Profiles"] = true
+L["Slash Command"] = true
+
+-- Config strings, general settings section
+L["OMEN_DESC"] = "Omen is a lightweight threat meter that shows you the threat of mobs you are engaged in combat with. You can change how Omen looks and behaves, and configure different profiles for every of your characters."
+L["Alpha"] = true
+L["Controls the transparency of the main Omen window."] = true
+L["Scale"] = true
+L["Controls the scaling of the main Omen window."] = true
+L["Frame Strata"] = true
+L["Controls the frame strata of the main Omen window. Default: MEDIUM"] = true
+L["Clamp To Screen"] = true
+L["Controls whether the main Omen window can be dragged offscreen"] = true
+L["Tells Omen to additionally check your 'focus' and 'focustarget' before your 'target' and 'targettarget' in that order for threat display."] = true
+L["Tells Omen to enter Test Mode so that you can configure Omen's display much more easily."] = true
+L["Collapse to show a minimum number of bars"] = true
+L["Lock Omen"] = true
+L["Locks Omen in place and prevents it from being dragged or resized."] = true
+L["Show minimap button"] = true
+L["Show the Omen minimap button"] = true
+L["Ignore Player Pets"] = true
+L["IGNORE_PLAYER_PETS_DESC"] = [[
+Tells Omen to skip enemy player pets when determining which unit to display threat data on.
+
+Player pets maintain a threat table when in |cffffff78Aggressive|r or |cffffff78Defensive|r mode and behave just like normal mobs, attacking the target with the highest threat. If the pet is instructed to attack a specific target, the pet still maintains the threat table, but sticks on the assigned target which by definition has 100% threat. Player pets can be taunted to force them to attack you.
+
+However, player pets on |cffffff78Passive|r mode do not have a threat table, and taunt does not work on them. They only attack their assigned target when instructed and do so without any threat table.
+
+When a player pet is instructed to |cffffff78Follow|r, the pet's threat table is wiped immediately and stops attacking, although it may immediately reacquire a target based on its Aggressive/Defensive mode.
+]]
+L["Click Through"] = true
+L["Makes the Omen window non-interactive"] = true
+L["Autocollapse"] = true
+L["Autocollapse Options"] = true
+L["Grow bars upwards"] = true
+L["Hide Omen on 0 bars"] = true
+L["Hide Omen entirely if it collapses to show 0 bars"] = true
+L["Max bars to show"] = true
+L["Max number of bars to show"] = true
+L["Background Options"] = true
+L["Background Texture"] = true
+L["Texture to use for the frame's background"] = true
+L["Border Texture"] = true
+L["Texture to use for the frame's border"] = true
+L["Background Color"] = true
+L["Frame's background color"] = true
+L["Border Color"] = true
+L["Frame's border color"] = true
+L["Tile Background"] = true
+L["Tile the background texture"] = true
+L["Background Tile Size"] = true
+L["The size used to tile the background texture"] = true
+L["Border Thickness"] = true
+L["The thickness of the border"] = true
+L["Bar Inset"] = true
+L["Sets how far inside the frame the threat bars will display from the 4 borders of the frame"] = true
+
+-- Config strings, title bar section
+L["Title Bar Settings"] = true
+L["Configure title bar settings."] = true
+L["Show Title Bar"] = true
+L["Show the Omen Title Bar"] = true
+L["Title Bar Height"] = true
+L["Height of the title bar. The minimum height allowed is twice the background border thickness."] = true
+L["Title Text Options"] = true
+L["The font that the title text will use"] = true
+L["The outline that the title text will use"] = true
+L["The color of the title text"] = true
+L["Control the font size of the title text"] = true
+L["Use Same Background"] = true
+L["Use the same background settings for the title bar as the main window's background"] = true
+L["Title Bar Background Options"] = true
+
+-- Config strings, show when... section
+L["Show When..."] = true
+L["Show Omen when..."] = true
+L["This section controls when Omen is automatically shown or hidden."] = true
+L["Use Auto Show/Hide"] = true
+L["Show Omen when any of the following are true"] = true
+L["You have a pet"] = true
+L["Show Omen when you have a pet out"] = true
+L["You are alone"] = true
+L["Show Omen when you are alone"] = true
+L["You are in a party"] = true
+L["Show Omen when you are in a 5-man party"] = true
+L["You are in a raid"] = true
+L["Show Omen when you are in a raid"] = true
+L["However, hide Omen if any of the following are true (higher priority than the above)."] = true
+L["You are resting"] = true
+L["Turning this on will cause Omen to hide whenever you are in a city or inn."] = true
+L["You are in a battleground"] = true
+L["Turning this on will cause Omen to hide whenever you are in a battleground or arena."] = true
+L["You are not in combat"] = true
+L["Turning this on will cause Omen to hide whenever you are not in combat."] = true
+L["AUTO_SHOW/HIDE_NOTE"] = "Note: If you manually toggle Omen to show or hide, it will remain shown or hidden regardless of Auto Show/Hide settings until you next zone, join or leave a party or raid, or change any Auto Show/Hide settings."
+
+-- Config strings, show classes... section
+L["Show Classes..."] = true
+L["SHOW_CLASSES_DESC"] = "Show Omen threat bars for the following classes. The classes here refer to those people in your party/raid only with the exception of the 'Not In Party' option."
+L["Show bars for these classes"] = true
+L["DEATHKNIGHT"] = "Death Knight"
+L["DRUID"] = "Druid"
+L["HUNTER"] = "Hunter"
+L["MAGE"] = "Mage"
+L["PALADIN"] = "Paladin"
+L["PET"] = "Pet"
+L["PRIEST"] = "Priest"
+L["ROGUE"] = "Rogue"
+L["SHAMAN"] = "Shaman"
+L["WARLOCK"] = "Warlock"
+L["WARRIOR"] = "Warrior"
+L["HERO"] = "Hero"
+L["*Not in Party*"] = true
+
+-- Config strings, bar settings section
+L["Bar Settings"] = true
+L["Configure bar settings."] = true
+L["Animate Bars"] = true
+L["Smoothly animate bar changes"] = true
+L["Short Numbers"] = true
+L["Display large numbers in Ks"] = true
+L["Bar Texture"] = true
+L["The texture that the bar will use"] = true
+L["Bar Height"] = true
+L["Height of each bar"] = true
+L["Bar Spacing"] = true
+L["Spacing between each bar"] = true
+L["Show TPS"] = true
+L["Show threat per second values"] = true
+L["TPS Window"] = true
+L["TPS_WINDOW_DESC"] = "The threat per second calculation is based on a real time sliding window of the last X seconds."
+L["Show Threat Values"] = true
+L["Show Threat %"] = true
+L["Show Headings"] = true
+L["Show column headings"] = true
+L["Heading BG Color"] = true
+L["Heading background color"] = true
+L["Use 'My Bar' color"] = true
+L["Use a different colored background for your threat bar in Omen"] = true
+L["'My Bar' BG Color"] = true
+L["The background color for your threat bar"] = true
+L["Use Tank Bar color"] = true
+L["Use a different colored background for the tank's threat bar in Omen"] = true
+L["Tank Bar Color"] = true
+L["The background color for your tank's threat bar"] = true
+L["Show Pull Aggro Bar"] = true
+L["Show a bar for the amount of threat you will need to reach in order to pull aggro."] = true
+L["Pull Aggro Bar Color"] = true
+L["The background color for your Pull Aggro bar"] = true
+L["Use Class Colors"] = true
+L["Use standard class colors for the background color of threat bars"] = true
+L["Pet Bar Color"] = true
+L["The background color for pets"] = true
+L["Use !ClassColors"] = true
+L["Use !ClassColors addon for class colors for the background color of threat bars"] = true
+L["Fade/MI Bar Color"] = true
+L["The background color for players under the effects of Fade and Mirror Image (they will be at negative 4 million threat)"] = true
+L["Bar BG Color"] = true
+L["The background color for all threat bars"] = true
+L["Always Show Self"] = true
+L["Always show your threat bar on Omen (ignores class filter settings), showing your bar on the last row if necessary"] = true
+L["Invert Bar/Text Colors"] = true
+L["Switch the colors so that the bar background colors and the text colors are swapped."] = true
+L["Bar Label Options"] = true
+L["Font"] = true
+L["The font that the labels will use"] = true
+L["Font Size"] = true
+L["Control the font size of the labels"] = true
+L["Font Color"] = true
+L["The color of the labels"] = true
+L["Font Outline"] = true
+L["The outline that the labels will use"] = true
+L["None"] = true
+L["Outline"] = true
+L["Thick Outline"] = true
+
+-- Config strings, slash command section
+L["OMEN_SLASH_DESC"] = "These buttons execute the same functions as the ones in the slash command /omen"
+L["Toggle Omen"] = true
+L["Center Omen"] = true
+L["Configure"] = true
+L["Open the configuration dialog"] = true
+L["Show Omen"] = true
+
+-- Config strings, warning settings section
+L["Warning Settings"] = true
+L["OMEN_WARNINGS_DESC"] = "This section allows you to customize when and how Omen notifies you if you are about to pull aggro."
+L["Enable Sound"] = true
+L["Causes Omen to play a chosen sound effect"] = true
+L["Enable Screen Flash"] = true
+L["Causes the entire screen to flash red momentarily"] = true
+L["Enable Screen Shake"] = true
+L["Causes the entire game world to shake momentarily. This option only works if nameplates are turned off."] = true
+L["Enable Warning Message"] = true
+L["Print a message to screen when you accumulate too much threat"] = true
+L["Warning Threshold %"] = true
+L["Sound to play"] = true
+L["Disable while tanking"] = true
+L["DISABLE_WHILE_TANKING_DESC"] = "Do not give out any warnings if Defensive Stance, Bear Form, Righteous Fury or Frost Presence is active."
+L["Test warnings"] = true
+
+-- Config strings, for Fubar
+L["Click|r to toggle the Omen window"] = true
+L["Right-click|r to open the options menu"] = true
+L["FuBar Options"] = true
+L["Attach to minimap"] = true
+L["Hide minimap/FuBar icon"] = true
+L["Show icon"] = true
+L["Show text"] = true
+L["Position"] = true
+L["Left"] = true
+L["Center"] = true
+L["Right"] = true
+
+-- FAQ
+L["Help File"] = true
+L["A collection of help pages"] = true
+L["FAQ Part 1"] = true
+L["FAQ Part 2"] = true
+L["Frequently Asked Questions"] = true
+L["Warrior"] = true
+
+L["GENERAL_FAQ"] = [[
+|cffffd200How is Omen3 different from Omen2?|r
+
+Omen3 relies completely on the Blizzard threat API and threat events. It does not attempt to calculate or extrapolate threat unlike Omen2.
+
+Omen2 used what we called the Threat-2.0 library. This library was responsible for monitoring the combat log, spellcasting, buffs, debuffs, stances, talents and gear modifiers for calculating each individuals threat. Threat was calculated based on what was known or approximated from observed behaviors. Many abilities such as knockbacks were just assumed (to be a 50% threat reduction) as they were mostly impossible to confirm.
+
+The Threat-2.0 library also included addon communication to broadcast your threat to the rest of the raid as long as they were also using Threat-2.0. This data was then used to provide a raid wide display of threat information.
+
+Since patch 3.0.2, Omen no longer does any of these things and the need for a threat library is no longer necessary. Omen3 uses Blizzard's new in-built threat monitor to obtain exact values of every members threat. This means Omen3 has no need for synchronisation of data, combat log parsing or guessing, resulting in a significant increase in performance with regards to network traffic, CPU time and memory used. The implementation of boss modules for specific threat events (such as Nightbane wiping threat on landing) are also no longer necessary.
+
+Further benefits of this new implementation include the addition of NPC threat on a mob (eg, Human Kalecgos). However, there are some drawbacks; frequency of updates are much slower, threat details cannot be obtained unless somebody in your party/raid are targetting the mob and it is also not possible to obtain threat from a mob you are not in direct combat with.
+
+|cffffd200How do I get rid of the 2 vertical gray lines down the middle?|r
+
+Lock your Omen. Locking Omen will prevent it from being moved or resized, as well as prevent the columns from being resized. If you haven't realized it, the 2 vertical gray lines are column resizing handles.
+
+|cffffd200How do I make Omen3 look like Omen2?|r
+
+Change the both the Background Texture and Border Texture to Blizzard Tooltip, change the Background Color to black (by dragging the luminance bar to the bottom), and the Border Color to blue.
+
+|cffffd200Why does no threat data show on a mob when I target it even though it is in combat?|r
+
+The Blizzard threat API does not return threat data on any mob you are not in direct combat with. We suspect this is an effort on Blizzard's part to save network traffic.
+
+|cffffd200Is there ANY way around this Blizzard limitation? Not being able to see my pet's threat before I attack has set me back to guessing.|r
+
+There is no way around this limitation short of us doing the guessing for you (which is exactly how Omen2 did it).
+
+The goal of Omen3 is to provide accurate threat data, we no longer intend to guess for you and in the process lower your FPS. Have some confidence in your pet/tank, or just wait 2 seconds before attacking and use a low damage spell such as Ice Lance so that you can get initial threat readings.
+]]
+L["GENERAL_FAQ2"] = [[
+|cffffd200Can we get AoE mode back?|r
+
+Again, this is not really possible without guessing threat values. Blizzard's threat API only allows us to query for threat data on units that somebody in the raid is targetting. This means that if there are 20 mobs and only 6 of them are targetted by the raid, there is no way to obtain accurate threat data on the other 14.
+
+This is also extremely complicated to guess particularly for healing and buffing (threat gets divided by the number of mobs you are in combat with) because mobs that are under crowd control effects (sheep, banish, sap, etc) do not have their threat table modified and addons cannot reliably tell how many mobs you are in combat with. Omen2's guess was almost always wrong.
+
+|cffffd200The tooltips on unit mouseover shows a threat % that does not match the threat % reported by Omen3. Why?|r
+
+Blizzard's threat percentage is scaled to between 0% and 100%, so that you will always pull aggro at 100%. Omen reports the raw unscaled values which has pulling aggro percentages at 110% while in melee range and 130% otherwise.
+
+By universal agreement, the primary target of a mob is called the tank and is defined to be at 100% threat.
+
+|cffffd200Does Omen3 sync or parse the combat log?|r
+
+No. Omen3 does not attempt to sync or parse the combat log. Currently there are no intentions to do so.
+
+|cffffd200The threat updates are slow...|r
+
+Omen3 updates the threat values you see as often as Blizzard updates the threat values to us.
+
+In fact, Blizzard updates them about once per second, which is much faster than what Omen2 used to sync updates. In Omen2, you only transmitted your threat to the rest of the raid once every 3 seconds (or 1.5s if you were a tank).
+
+|cffffd200Where can I report bugs or give suggestions?|r
+
+http://forums.wowace.com/showthread.php?t=14249
+
+|cffffd200Who wrote Omen3?|r
+
+Xinhuan (Blackrock US Alliance) did.
+
+|cffffd200Do you accept Paypal donations?|r
+
+Yes, send to xinhuan AT gmail DOT com.
+]]
+L["WARRIOR_FAQ"] = [[The following data is obtained from |cffffd200http://www.tankspot.com/forums/f200/39775-wow-3-0-threat-values.html|r on 2nd Oct 2008 (credits to Satrina). The numbers are for a level 80.
+
+|cffffd200Modifiers|r
+Battle Stance ________ x 80
+Berserker Stance _____ x 80
+Tactical Mastery _____ x 121/142/163
+Defensive Stance _____ x 207.35
+
+Note that in our original threat estimations (that we use now in WoW 2.0), we equated 1 damage to 1 threat, and used 1.495 to represent the stance+defiance multiplier. We see that Blizzard's method is to use the multiplier without decimals, so in 2.x it would've been x149 (maybe x149.5); it is x207 (maybe 207.3) in 3.0. I expect that this is to allow the transport of integer values instead of decimal values across the Internet for efficiency. It appears that threat values are multiplied by 207.35 at the server, then rounded.
+
+If you still want to use the 1 damage = 1 threat method, the stance modifiers are 0.8 and 2.0735, etc.
+
+|cffffd200Threat Values (stance modifiers apply unless otherwise noted):|r
+Battle Shout _________ 78 (split)
+Cleave _______________ damage + 225 (split)
+Commanding Shout _____ 80 (split)
+Concussion Blow ______ damage only
+Damage Shield ________ damage only
+Demoralising Shout ___ 63 (split)
+Devastate ____________ damage + 5% of AP *** Needs re-checking for 8982 **
+Dodge/Parry/Block_____ 1 (in defensive stance with Improved Defensive Stance only)
+Heroic Strike ________ damage + 259
+Heroic Throw _________ 1.50 x damage
+Rage Gain ____________ 5 (stance modifier is not applied)
+Rend _________________ damage only
+Revenge ______________ damage + 121
+Shield Bash __________ 36
+Shield Slam __________ damage + 770
+Shockwave ____________ damage only
+Slam _________________ damage + 140
+Spell Reflect ________ damage only (only for spells aimed at you)
+Social Aggro _________ 0
+Sunder Armour ________ 345 + 5%AP
+Thunder Clap _________ 1.85 x damage
+Vigilance ____________ 10% of target's generated threat (stance modifier is not applied)
+
+You do not gain threat for reflecting spells targetted at allies with Improved Spell Reflect. When you reflect a spell for an ally, your ally gains the threat for the damage dealt by the reflected spell.
+]]
+
diff --git a/Localization/esES.lua b/Localization/esES.lua
index 0379290..aa0df03 100644
--- a/Localization/esES.lua
+++ b/Localization/esES.lua
@@ -1,10 +1,334 @@
--- THIS CONTENTS OF THIS FILE IS AUTO-GENERATED BY THE WOWACE PACKAGER
--- Please use the Localization App on WoWAce to update this
--- at http://www.wowace.com/projects/omen-threat-meter/localization/
-
--- Translation courtesy of Ben (Aesyl - US Tanaris)
-local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
-local L = AceLocale:NewLocale("Omen", "esES") or AceLocale:NewLocale("Omen", "esMX")
-if not L then return end
-
---@localization(locale="esES", format="lua_additive_table", table-name="L", handle-unlocalized="comment")@
+-- THIS CONTENTS OF THIS FILE IS AUTO-GENERATED BY THE WOWACE PACKAGER
+-- Please use the Localization App on WoWAce to update this
+-- at http://www.wowace.com/projects/omen-threat-meter/localization/
+
+-- Translation courtesy of Ben (Aesyl - US Tanaris)
+local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
+local L = AceLocale:NewLocale("Omen", "esES") or AceLocale:NewLocale("Omen", "esMX")
+if not L then return end
+
+L["A collection of help pages"] = "Una colección de páginas de ayuda"
+L["Alpha"] = "Transparencia"
+L["Always Show Self"] = "Siempre verse uno mismo"
+L["Always show your threat bar on Omen (ignores class filter settings), showing your bar on the last row if necessary"] = "Siempre ver tu barra de amenaza en Omen (ignora las opciones de filtrado de clase), enseña tu barra en la última fila si es necesario"
+L["Animate Bars"] = "Animar las Barras"
+L["Attach to minimap"] = "Adjuntar a la minimapa"
+L["Autocollapse"] = "Autocompactarse"
+L["Autocollapse Options"] = "Opciones de autocompactación"
+L["AUTO_SHOW/HIDE_NOTE"] = "Nota: Si usted muestra/oculta Omen manualmente, éste permanecerá oculto o mostrándose, independientemente de los ajustes de Auto-Mostrar/Ocultar hasta que llegues a la próxima zona, te unas o abandones un grupo o banda, o cambies cualquier ajuste de Auto-Mostrar/Ocultar."
+L["Background Color"] = "Color del Fondo"
+L["Background Options"] = "Opciones del Fondo"
+L["Background Texture"] = "Textura del Fondo"
+L["Background Tile Size"] = "Tamaño del mosaico de fondo"
+L["Bar BG Color"] = "Color de Fondo de Barra"
+L["Bar Height"] = "Altura de la Barra"
+L["Bar Inset"] = "Recuadro de la Barra"
+L["Bar Label Options"] = "Opciones de Etiqueta de Barra"
+L["Bar Settings"] = "Opciones de las Barras"
+L["Bar Spacing"] = "Espaciado entre Barras"
+L["Bar Texture"] = "Textura de la Barra"
+L["Border Color"] = "Color del Borde"
+L["Border Texture"] = "Textura del Marco"
+L["Border Thickness"] = "Ancho del Borde"
+L["Causes Omen to play a chosen sound effect"] = "Hacer que Omen reproduzca un efecto de sonido escogido"
+L["Causes the entire game world to shake momentarily. This option only works if nameplates are turned off."] = "Hace que toda la pantalla del juego tiemble brevemente. Esta opción sólo funciona si las placas de nombre son desactivadas."
+L["Causes the entire screen to flash red momentarily"] = "Hace que la pantalla entera destelle de rojo brevemente"
+L["Center"] = "Centro"
+L["Center Omen"] = "Centrar Omen"
+L["|cffff0000Error:|r Omen cannot use shake warning if you have turned on nameplates at least once since logging in."] = "|cffff0000Error:|r Omen no puede usar el aviso de temblor si usted ha activado las placas de nombre al menos una vez desde que ha entrado."
+L["Clamp To Screen"] = "Anclar a la pantalla"
+L["Click|r to toggle the Omen window"] = "Clickea|r para mostrar/ocultar la ventana de Omen"
+-- L["Click Through"] = "Click Through"
+L["Collapse to show a minimum number of bars"] = "Compactarse para mostrar un número mínimo de barras."
+L["Configure"] = "Configurar"
+L["Configure bar settings."] = "Configurar las Opciones de Barra"
+L["Configure title bar settings."] = "Configurar opciones de la Barra de Título"
+L["Controls the frame strata of the main Omen window. Default: MEDIUM"] = "Controla la capa donde se sitúa la ventana principal de Omen. Capa por defecto: Media"
+L["Controls the scaling of the main Omen window."] = "Controla la escala de la ventana principal de Omen."
+L["Controls the transparency of the main Omen window."] = "Controla la transparencia de la ventana principal de Omen."
+L["Controls whether the main Omen window can be dragged offscreen"] = "Controla si la ventana principal de Omen puede ser movida fuera de la pantalla"
+L["Control the font size of the labels"] = "Controla el tamaño de la fuente de las etiquetas"
+L["Control the font size of the title text"] = "Controla el tamaño de la fuente del texto del título"
+L["DEATHKNIGHT"] = "Caballero de la Muerte"
+L["Disable while tanking"] = "Desactivar cuándo eres el tanque"
+L["DISABLE_WHILE_TANKING_DESC"] = "No dar ninguna alerta si Actitud defensiva, Forma de oso, Furia recta o Presencia de Escarcha está activado."
+L["Display large numbers in Ks"] = "Mostrar números grandes en Ks"
+L["DRUID"] = "Druida"
+L["HERO"] = "Hero"
+L["Enable Screen Flash"] = "Activar Destello de Pantalla"
+L["Enable Screen Shake"] = "Activar Temblor de Pantalla"
+L["Enable Sound"] = "Activar Sonido"
+L["Enable Warning Message"] = "Activar Mensaje de Aviso"
+L["Fade/MI Bar Color"] = "Color de la barra de Sigilo/Reflejo exacto"
+L["FAQ Part 1"] = "FAQ Parte 1"
+L["FAQ Part 2"] = "FAQ Parte 2"
+L["Font"] = "Fuente"
+L["Font Color"] = "Color de Fuente"
+L["Font Outline"] = "Borde de Fuente"
+L["Font Size"] = "Tamaño de Fuente"
+L["Frame's background color"] = "Color de Fondo del Marco"
+L["Frame's border color"] = "Color del Borde del Marco"
+L["Frame Strata"] = "Capa del Marco"
+L["Frequently Asked Questions"] = "Preguntas Frecuentes (FAQ)"
+L["FuBar Options"] = "Opciones de FuBar"
+L["GENERAL_FAQ"] = [=[|cffffd200¿Qué diferencias hay entre Omen3 y Omen2?|r
+
+Omen3 cuenta completamente con el API de amenaza de Blizzard y eventos de amenaza. A diferencia de Omen2, no trata de calcular o extrapolar la amenaza.
+
+Omen2 era lo que llamábamos la biblioteca Threat-2.0. Esta biblioteca era responsable de observar el diario de combate, hechizos, ventajas, desventajas, actitudes, talentos y modificadores de equipo para calcular la amenaza de cada una individualmente. La amenaza se calculaba basándose en informaciones conocidas o aproximaciones de conductas observadas. Muchas informaciones, como los golpes por la espalda, se basaban en suposiciones.
+
+La biblioteca Threat-2.0 también incluía comunicación para transmitir su amenaza al resto de la banda mientras que usaban Threat-2.0 también. Esta información estaba usado para dar una muestra de información de amenaza incluyendo la banda entera.
+
+Desde el parche 3.0.2, Omen ya no hace estas cosas y una biblioteca de amenaza ya no es necesario. Omen3 usa el monitor de amenaza de Blizzard para obtener los valores exactos de la amenaza de cada miembro. Esto significa que Omen3 no necesita sincronización de datos, analizar el diario de combate o intentar adivinar. Esto conlleva un aumento significativo en rendimiento con respecto al tráfico de red, uso de la CPU y de la memoria. La implementación de módulos por jefes específicos ya no es necesaria.
+
+Además esta implementación nueva permite la adición de la amenaza de NPCs. Sin embargo, hay desventajas; las actualizaciones son menos frecuentes, los detalles de amenaza no pueden obtenerse a menos que alguien en su banda esté apuntando al enemigo, y no es posible obtener amenaza de un enemigo con el que usted no está en combate.
+
+|cffffd200¿Cómo me deshago de las 2 líneas verticales de color gris de la mitad?|r
+
+Bloquea tu Omen. Bloquear Omen evita que sea movido o reescalado, como también previene que el tamaño de las columnas sea cambiado. Si no realizas esto, las 2 líneas grises verticales seguirán ahí para poder cambiar las columnas manualmente.
+
+|cffffd200¿Cómo hago que Omen3 se parezca a Omen2?|r
+
+Cambia la Textura de Fondo y Textura de Borde a "Blizzard Tooltip", cambia el Color de Fondo a negro (arrastra hacia abajo la barra de luminosidad), y el Color de Borde a azul.
+
+|cffffd200¿Por qué no me muestra la amenaza en un enemigo cuándo lo apunto, aún cuando él está en combate?|r
+
+El API de amenaza de Blizzard no devuelve datos de amenaza de un enemigo con el que no estás en combate directamente, porque no estarás en su lista de amenaza. Creemos que esto es un esfuerzo para reducir tráfico de red.
+
+|cffffd200¿Hay forma de sortear esta limitación de Blizzard? No poder ver la amenaza de mi pet antes de que yo ataque me obliga a especular.|r
+
+No hay forma de sortear esta limitación excepto que nosotros especulemos por tí (que es exactamente lo que hacía Omen2).
+
+El objetivo de Omen3 es proveer información fiable sobre la amenaza, ya no intentamos especular por tí y en el camino reducir tus FPS. Ten algo de confianza en tu pet/tanque, o sólo espera 2 segundos antes de atacar y usa un hechizo de poco daño como Lanza de Hielo para que puedas obtener unas lecturas iniciales de amenaza.
+
+|cffffd200¿Qué es la amenaza, agro y APS?|r
+
+La amenaza, o puntos de amenaza, es un valor que un personaje puede tener sobre un enemigo NPC/enemigo (personaje no controlable, es decir, no es un jugador). Este valor aumenta normalmente 1 punto de amenaza por cada 1 punto de daño sobre el enemigo ó 2 puntos de cura sobre un jugador (los buffs y debuffs, mejoras y enfermedades en el tiempo, también aumentan la amenaza). Los valores pueden aumentar o disminuir dependiendo de la habilidad usada. Los APS son sencillamente los puntos de amenaza generados por segundo (parecido a DPS, daño por segundo).
+
+El agro es el hecho de tener a un enemigo atacándote. Para cambiar el agro desde un jugador a otro, el otro jugador debe conseguir un 30% más de amenaza del jugador que tiene el agro actualmente (esto hace que sea relativamente fácil mantener el agro, pero una vez perdido sea muy difícil recuperarlo).]=]
+L["GENERAL_FAQ2"] = [=[|cffffd200¿Podemos obtener de nuevo el modo de AOE?
+
+De nuevo, esto no es realmente posible sin la amenaza de adivinanzas valores. La amenaza de la API de Blizzard sólo nos permite una consulta de datos sobre las unidades de la amenaza de que alguien en el ataque se dirige. Esto significa que si hay 20 bandas y sólo 6 de ellos son el blanco de la redada, no hay manera de obtener datos exactos sobre la amenaza de otros 14.
+
+Esto también es extremadamente complicado de adivinar sobre todo para la curación y el desbarbado (amenaza se divide por el número de bandas que están en combate con), ya que las multitudes que se encuentran bajo efectos de control de masas (oveja, desterrar, savia, etc) no tienen su amenaza de mesa modificados addons y fiable no puede decir cuántas bandas se encuentra en combate con. Omen2 de adivinar casi siempre se equivocan.
+
+|cffffd200Herramientas en la unidad mouseover muestra una amenaza% que no coincide con la amenaza% reportado por Omen3. ¿Por qué?
+
+Blizzard es la amenaza porcentaje a escala entre el 0% y 100%, para que siempre tire agresividad en un 100%. Presagio informes valores de la materia prima sin escala que ha tirando agresividad en porcentajes del 110% mientras que en cuerpo a cuerpo y rango de 130% lo contrario.
+
+Por acuerdo universal, el objetivo principal de un grupo que se llama el depósito y se define como la amenaza al 100%.
+
+|cffffd200¿Tiene Omen3 sincronización o analizar la lucha contra el registro?
+
+No. Omen3 no trata de analizar la sincronización o la lucha registro. Actualmente no hay intenciones de hacerlo.
+
+|cffffd200Las actualizaciones de Omen son lentas ...
+
+Omen3 actualizaciones de los valores de la amenaza que aparezca tan a menudo como Blizzard actualiza los valores que nos amenaza.
+
+De hecho, Blizzard actualiza aproximadamente una vez por segundo, que es mucho más rápido que lo Omen2 usar para sincronizar las actualizaciones. En Omen2, sólo transmitió su amenaza para el resto de la redada una vez cada 3 segundos (o si se 1.5s un tanque).
+
+|cffffd200¿Dónde puedo reportar errores o hacer sugerencias?
+
+http://forums.wowace.com/showthread.php?t=14249
+
+|cffffd200¿Quién diseñó Omen3?
+
+El diseñador principal de Omen es Xinhuan (Reino Blackrock de EE.UU., Alianza).
+
+¿Aceptan donaciones de Paypal?
+
+Sí, por favor, enviadlas a "xinhuan @ gmail 'PUNTO' com".]=]
+L["General Settings"] = "Opciones Generales"
+L["Grow bars upwards"] = "Barras crecen hacia arriba"
+L["Heading background color"] = "Color de Fondo de las Categorías"
+L["Heading BG Color"] = "Color de Fondo de las Categorías"
+L["Height of each bar"] = "Altura de cada barra"
+L["Height of the title bar. The minimum height allowed is twice the background border thickness."] = "Altura de la Barra de Título. La altura mínima permitida es el doble del grosor del borde del fondo."
+L["Help File"] = "Archivo de ayuda"
+L["Hide minimap/FuBar icon"] = "Ocultar el icono del minimapa/FuBar"
+L["Hide Omen"] = "Ocultar Omen"
+L["Hide Omen entirely if it collapses to show 0 bars"] = "Ocultar Omen completamente si se desploma para mostrar 0 barras"
+L["Hide Omen on 0 bars"] = "Ocultar Omen cuando hay 0 barras"
+L["However, hide Omen if any of the following are true (higher priority than the above)."] = "Sin embargo, ocultar Omen si alguna de las siguientes son verdaderas (prioridad más alta que lo anterior)."
+L["HUNTER"] = "Cazador"
+L["Ignore Player Pets"] = "Ignorar tu Mascota"
+L["IGNORE_PLAYER_PETS_DESC"] = [=[Tells Omen to skip enemy player pets when determining which unit to display threat data on.
+
+Player pets maintain a threat table when in |cffffff78Aggressive|r or |cffffff78Defensive|r mode and behave just like normal mobs, attacking the target with the highest threat. If the pet is instructed to attack a specific target, the pet still maintains the threat table, but sticks on the assigned target which by definition has 100% threat. Player pets can be taunted to force them to attack you.
+
+However, player pets on |cffffff78Passive|r mode do not have a threat table, and taunt does not work on them. They only attack their assigned target when instructed and do so without any threat table.
+
+When a player pet is instructed to |cffffff78Follow|r, the pet's threat table is wiped immediately and stops attacking, although it may immediately reacquire a target based on its Aggressive/Defensive mode.
+]=]
+L["Invert Bar/Text Colors"] = "Invertir Barras/Colores de texto"
+L["Left"] = "Izquierda"
+L["Lock Omen"] = "Bloquear Omen"
+L["Locks Omen in place and prevents it from being dragged or resized."] = "Bloquea Omen en el sitio y lo evita que arrastrar o cambiar la talla."
+L["MAGE"] = "Mago"
+-- L["Makes the Omen window non-interactive"] = "Makes the Omen window non-interactive"
+L["Max bars to show"] = "Máx. barras para mostrar"
+L["Max number of bars to show"] = "Nº Máx. de barras mostradas"
+L["'My Bar' BG Color"] = "Color de fondo de tu barra"
+L["Name"] = "Nombre"
+L["None"] = "Nada"
+L["*Not in Party*"] = "*No Está en el Grupo*"
+L["OMEN_DESC"] = "Omen es un medidor ligero de amenaza que le muestra la amenaza del enemigo con el que usted está en combate. Puede configurar cómo Omen se ve y funciona, y configurar perfiles diferentes para cada personaje."
+L["Omen Quick Menu"] = "Menu Rápido de Omen"
+L["OMEN_SLASH_DESC"] = "Estos botones ejecutan las mismas funciones que las del comando de chat /omen"
+L["OMEN_WARNINGS_DESC"] = "Esta sección le permite configurar cuándo y cómo Omen le notifica si su amenaza es demasiada alta o si recibes el aggro."
+L["Open Config"] = "Abrir Configuración"
+L["Open Omen's configuration panel"] = "Abrir el panel de configuración de Omen"
+L["Open the configuration dialog"] = "Abrir el diálogo de configuración"
+L["Outline"] = "Borde"
+L["PALADIN"] = "Paladín"
+L["Passed %s%% of %s's threat!"] = "¡Has pasado un %s%% del %s de amenaza!"
+L["PET"] = "Mascota"
+L["Pet Bar Color"] = "Color de la barra de mascotas"
+L["Position"] = "Posición"
+L["PRIEST"] = "Sacerdote"
+L["Print a message to screen when you accumulate too much threat"] = "Mostrar una mensaje en pantalla cuándo acumulas demasiada amenaza"
+L["Profiles"] = "Perfiles"
+L["> Pull Aggro <"] = ">> Amenaza para Aggro <<"
+L["Pull Aggro Bar Color"] = "Color de la barra de Amenaza para Aggro"
+L["Right"] = "Derecho"
+L["Right-click|r to open the options menu"] = "Haga clic con el botón derecho|r para abrir el menú de opciones"
+L["ROGUE"] = "Pícaro"
+L["Scale"] = "Escala"
+L["Sets how far inside the frame the threat bars will display from the 4 borders of the frame"] = "Controla a qué distancia de los 4 bordes del marco se situarán las barras de amenaza"
+L["SHAMAN"] = "Chamán"
+L["Short Numbers"] = "Números Cortos"
+L["Show a bar for the amount of threat you will need to reach in order to pull aggro."] = "Mostrar una barra que indica la amenaza que necesitas para conseguir el aggro y te ataquen (Amenaza para Aggro)"
+L["Show bars for these classes"] = "Mostrar barras para estas clases"
+L["Show Classes..."] = "Mostrar Clases..."
+L["SHOW_CLASSES_DESC"] = "Mostrar barras de amenaza de Omen de las clases siguientes. Las clases aquí se refieren solamente a las personas quien están en su grupo/banda a excepción de la opción 'No Está en el Grupo'."
+L["Show column headings"] = "Mostrar las categorías sobre las columnas"
+L["Show Headings"] = "Mostrar Categorías"
+L["Show icon"] = "Mostrar icono"
+L["Show minimap button"] = "Mostrar Icono del Minimapa"
+L["Show Omen"] = "Mostrar Omen"
+L["Show Omen when..."] = "Mostrar Omen cuándo..."
+L["Show Omen when any of the following are true"] = "Mostrar Omen cuándo cualquiera de lo siguiente es cierto"
+L["Show Omen when you are alone"] = "Mostrar Omen cuándo usted está solo(a)"
+L["Show Omen when you are in a 5-man party"] = "Mostrar Omen cuándo usted está en un grupo de 5 miembros"
+L["Show Omen when you are in a raid"] = "Mostrar Omen cuándo usted está en una banda"
+L["Show Omen when you have a pet out"] = "Mostrar Omen cuándo tienes una mascota fuera"
+L["Show Pull Aggro Bar"] = "Mostrar Barra de Amenaza para Aggro"
+L["Show text"] = "Mostrar Texto"
+L["Show the Omen minimap button"] = "Mostrar el icono de Omen en el minimapa"
+L["Show the Omen Title Bar"] = "Mostrar la barra de título de Omen"
+L["Show Threat %"] = "Mostrar el Porcentaje de Amenaza"
+L["Show threat per second values"] = "Mostrar la amenaza por segundo (APS)"
+L["Show Threat Values"] = "Mostrar Valores de Amenaza"
+L["Show Title Bar"] = "Mostrar la Barra de Título"
+L["Show TPS"] = "Mostrar APS (Amenaza por Segundo)"
+L["Show When..."] = "Mostrar cuándo..."
+L["Slash Command"] = "Comando de Barra"
+L["Smoothly animate bar changes"] = "Animar Suavamente las Barras"
+L["Sound to play"] = "Sonido para reproducir"
+L["Spacing between each bar"] = "Espacio entre cada barra"
+L["Switch the colors so that the bar background colors and the text colors are swapped."] = "Cambiar los colores de modo que el color de fondo de las barras y el texto estén invertidas."
+L["Tank Bar Color"] = "Color de la Barra del Tanque"
+L["Tells Omen to additionally check your 'focus' and 'focustarget' before your 'target' and 'targettarget' in that order for threat display."] = "Indica a Omen que adicionalmente revise tu 'foco' y 'objetivo de foco' antes que tu 'objetivo' y 'objetivo de objetivo\" en ese orden en la ventana de amenaza."
+L["Tells Omen to enter Test Mode so that you can configure Omen's display much more easily."] = "Omen entra en el Modo de prueba para que puedas configurar la ventana de de Omen más fácilmente."
+L["Test Mode"] = "Modo de prueba"
+L["Test warnings"] = "Avisos de Prueba"
+L["Texture to use for the frame's background"] = "Textura usada en el fondo del marco"
+L["Texture to use for the frame's border"] = "Textura usada en el borde del marco"
+L["The background color for all threat bars"] = "El color de fondo para todas las barras de amenaza"
+L["The background color for pets"] = "El color de fondo para las barras de mascotas"
+L["The background color for players under the effects of Fade and Mirror Image (they will be at negative 4 million threat)"] = "El color de fondo para jugadores bajo los efectos de Desvanecerse y Reflejo exacto (que será negativo en 4 millones de amenaza)"
+L["The background color for your Pull Aggro bar"] = "El color de fondo para tu barra de Amenaza para Aggro"
+L["The background color for your tank's threat bar"] = "El color de fondo de la barra de amenaza del tanque"
+L["The background color for your threat bar"] = "El color de fondo de tu barra de amenaza"
+L["The color of the labels"] = "El color de las etiquetas"
+L["The color of the title text"] = "El color del texto del título"
+L["The font that the labels will use"] = "La fuente que las etiquetas usarán"
+L["The font that the title text will use"] = "La fuente que el título usará"
+L["The outline that the labels will use"] = "El borde que las etiquetas usarán"
+L["The outline that the title text will use"] = "El borde que el texto del título usará"
+L["The size used to tile the background texture"] = "El tamaño usado la textura del mosaico del fondo"
+L["The texture that the bar will use"] = "La Textura que la barra usará"
+L["The thickness of the border"] = "El grosor del borde"
+L["Thick Outline"] = "Borde ancho"
+L["This section controls when Omen is automatically shown or hidden."] = "Esta sección controla cuando Omen se muestra u oculta automáticamente"
+L["Threat"] = "Amenaza"
+L["Threat [%]"] = "Amenaza [%]"
+L["Tile Background"] = "Poner en mosaico el Fondo"
+L["Tile the background texture"] = "Poner en mosaico la textura de fondo"
+L["Title Bar Background Options"] = "Opciones del Fondo de la Barra de Título"
+L["Title Bar Height"] = "Altura de la Barra de Título"
+L["Title Bar Settings"] = "Opciones de la Barra de Título"
+L["Title Text Options"] = "Opciones del Texto del Título"
+L["Toggle Focus"] = "Mostrar/Ocultar Foco"
+L["Toggle Omen"] = "Mostrar/Ocultar Omen"
+L["TPS"] = "APS"
+L["TPS Window"] = "Ventana de APS"
+L["TPS_WINDOW_DESC"] = "El cálculo de amenaza por segundo se basa en una ventana deslizante en tiempo real de los últimos X segundos."
+L["Turning this on will cause Omen to hide whenever you are in a battleground or arena."] = "Activar esto hace que Omen se oculte mientras estás en un campo de batalla o arena."
+L["Turning this on will cause Omen to hide whenever you are in a city or inn."] = "Activar esto hace que Omen se oculte cuando estés en una ciudad o posada."
+L["Turning this on will cause Omen to hide whenever you are not in combat."] = "Activar esto hace que Omen se oculte cuando usted no está en combate."
+L[""] = ""
+L["Use a different colored background for the tank's threat bar in Omen"] = "Usar un coloreado de fondo diferente para la barra de amenaza del tanque en Omen"
+L["Use a different colored background for your threat bar in Omen"] = "Usar un coloreado de fondo diferente para tu barra de amenaza en Omen"
+L["Use Auto Show/Hide"] = "Usar Auto-Mostrar/Ocultar"
+L["Use !ClassColors"] = "Usar !ClassColors"
+L["Use Class Colors"] = "Usar Colores de Clase"
+L["Use !ClassColors addon for class colors for the background color of threat bars"] = "Usar el addon !ClassColors para los colores de fondo de las barras de amenaza"
+L["Use Focus Target"] = "Usar Objetivo de Foco"
+L["Use 'My Bar' color"] = "Usar un color especial en tu barra"
+L["Use Same Background"] = "Usar el mismo fondo"
+L["Use standard class colors for the background color of threat bars"] = "Usar los colores de clase estándar para el color de fondo de las barras de amenaza"
+L["Use Tank Bar color"] = "Usar el color de Barra del Tanque"
+L["Use the same background settings for the title bar as the main window's background"] = "Usar las mismas opciones de fondo de la ventana principal en la barra de título"
+L["WARLOCK"] = "Brujo"
+L["Warning Settings"] = "Opciones de Aviso"
+L["Warning Threshold %"] = "Umbral de Aviso %"
+L["Warrior"] = "Guerrerro"
+L["WARRIOR"] = "Guerrerro"
+L["WARRIOR_FAQ"] = [=[La siguiente información está obtenida de |cffffd200http://www.tankspot.com/forums/f200/39775-wow-3-0-threat-values.html|r Oct 2nd 2008 (crédito a Satrina). Los números son por un personaje de nivel 80.
+
+|cffffd200Modificadores|r
+Actitud de batalla ________ x 80
+Actitud rabiosa _____ x 80
+Maestría táctica _____ x 121/142/163
+Actitud defensiva _____ x 207.35
+
+Nota que en nuestros estimaciones originales (que usamos ahora en WoW 2.0), comparábamos 1 daño a 1 amenaza, y usábamos 1.495 para representar el modificador de actitud+desafío multiplicador. Ahora vemos que el método de Blizzard es para usar el multiplicador sin decimales, así en 2.x habría estado x149 (quizás x149.5); es x207 (quizás 207.3) en 3.0. Supongo que esto es para permitir el transporte de números enteros en vez de decimales a través del internet por rendimiento. Se parece que los valores de amenaza son multiplicados por 207.35 al servidor, pues redondeado.
+
+Si quiere usar el 1 daño = 1 amenaza método todavía; los modificadores de actitud son 0.8 y 2.0735, etc..
+
+|cffffd200Valores de Amenaza (modificadores de actitud aplica a menos que dijera otra cosa):|r
+Grito de batalla _________ 78 (divido)
+Rajar _______________ daño + 225 (divido)
+Grito de orden _____ 80 (divido)
+Arremetida de conmoción ______ daño solamente
+Escudo de daño ________ daño solamente
+Grito desmoralizador ___ 63 (divido)
+Devastar ____________ daño + 5% of AP
+Esquivar/Parar/Bloquear_____ 1 (en actitud defensiva solamente con Actitud defensiva mejorada)
+Golpe heroico ________ daño + 259
+Tira heroico _________ 1.50 x daño
+Gana de ira ____________ 5 (sin modificador de actitud)
+Desgarrar _________________ daño solamente
+Revancha ______________ daño + 121
+Azote de escudo __________ 36
+Embate con escudo __________ daño + 770
+Onda de choque ____________ daño solamente
+Embate _________________ daño + 140
+Reflejo de hechizos ________ daño solamente
+Aggro Social _________ 0
+Hender armadura ________ 345 + 5%AP
+Atronar _________ 1.85 x daño
+Vigilancia ____________ 10% de la amenaza del blanco (sin modificador de actitud)
+
+No gana amenaza por reflejar hechizos apuntado a su aliados con Reflejo de hechizos mejorado. Cuándo refleja un hechizo por un aliado, el aliado gana la amenaza por el daño del hechizo.
+]=]
+L["You are alone"] = "Estás solo(a)"
+L["You are in a battleground"] = "Estás en un campo de batalla"
+L["You are in a party"] = "Estás en un grupo"
+L["You are in a raid"] = "Estás en una banda"
+L["You are not in combat"] = "No estás en combate"
+L["You are resting"] = "Estás en una ciudad o posada"
+L["You have a pet"] = "Tienes una mascota"
+
diff --git a/Localization/frFR.lua b/Localization/frFR.lua
index 7bc7a18..7834878 100644
--- a/Localization/frFR.lua
+++ b/Localization/frFR.lua
@@ -1,10 +1,332 @@
--- THIS CONTENTS OF THIS FILE IS AUTO-GENERATED BY THE WOWACE PACKAGER
--- Please use the Localization App on WoWAce to update this
--- at http://www.wowace.com/projects/omen-threat-meter/localization/
-
--- French localization file, by Pettigrow
-local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
-local L = AceLocale:NewLocale("Omen", "frFR")
-if not L then return end
-
---@localization(locale="frFR", format="lua_additive_table", table-name="L", handle-unlocalized="comment")@
+-- THIS CONTENTS OF THIS FILE IS AUTO-GENERATED BY THE WOWACE PACKAGER
+-- Please use the Localization App on WoWAce to update this
+-- at http://www.wowace.com/projects/omen-threat-meter/localization/
+
+-- French localization file, by Pettigrow
+local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
+local L = AceLocale:NewLocale("Omen", "frFR")
+if not L then return end
+
+L["A collection of help pages"] = "Une série de pages d'aide."
+L["Alpha"] = "Transparence"
+L["Always Show Self"] = "Tj afficher soi-même"
+L["Always show your threat bar on Omen (ignores class filter settings), showing your bar on the last row if necessary"] = "Affiche toujours votre barre de menace sur Omen (ignore les paramètres de filtrage des classes), sur la dernière ligne si cela est nécessaire."
+L["Animate Bars"] = "Barres animées"
+L["Attach to minimap"] = "Attacher à la minicarte"
+L["Autocollapse"] = "Réduire auto."
+L["Autocollapse Options"] = "Options de la réduction automatique"
+L["AUTO_SHOW/HIDE_NOTE"] = "Note : si vous affichez/masquez Omen manuellement, ce dernier restera affiché ou masqué sans se soucier des paramètres de Afficher/cacher auto. tant que vous n'avez pas changé de zone, rejoint ou quitté un groupe ou un raid, ou changer un des paramètres de Afficher/masquer auto."
+L["Background Color"] = "Couleur arrière-plan"
+L["Background Options"] = "Options de l'arrière-plan"
+L["Background Texture"] = "Texture de l'arrière-plan"
+L["Background Tile Size"] = "Taille des carreaux"
+L["Bar BG Color"] = "Couleur A-P des barres"
+L["Bar Height"] = "Hauteur des barres"
+L["Bar Inset"] = "Décalage des barres"
+L["Bar Label Options"] = "Options des labels des barres"
+L["Bar Settings"] = "Param. des barres"
+L["Bar Spacing"] = "Espacement des barres"
+L["Bar Texture"] = "Texture des barres"
+L["Border Color"] = "Couleur de la bordure"
+L["Border Texture"] = "Texture de la bordure"
+L["Border Thickness"] = "Épaisseur de la bordure"
+L["Causes Omen to play a chosen sound effect"] = "Fait jouer Omen un effet sonore choisi."
+L["Causes the entire game world to shake momentarily. This option only works if nameplates are turned off."] = "Secoue momentanément toute la fenêtre du jeu. Cette option ne fonctionne que si les \"nameplates\" sont désactivées."
+L["Causes the entire screen to flash red momentarily"] = "Fait clignoter les bords de l'écran en rouge momentanément."
+L["Center"] = "Centre"
+L["Center Omen"] = "Centrer Omen"
+L["|cffff0000Error:|r Omen cannot use shake warning if you have turned on nameplates at least once since logging in."] = "|cffff0000Erreur :|r Omen ne peut utiliser l'avertissement par secousse si vous avez activé les \"nameplates\" au moins une fois depuis votre connexion au jeu."
+L["Clamp To Screen"] = "Garder à l'écran"
+L["Click|r to toggle the Omen window"] = "Clic gauche|r pour afficher/cacher la fenêtre de Omen."
+L["Click Through"] = "Clic à travers"
+L["Collapse to show a minimum number of bars"] = "Réduit Omen afin de n'afficher qu'un nombre minimal de barres."
+L["Configure"] = "Configurer"
+L["Configure bar settings."] = "Configure les paramètres des barres."
+L["Configure title bar settings."] = "Configure les paramètres de la barre-titre."
+L["Controls the frame strata of the main Omen window. Default: MEDIUM"] = "Contrôle la couche de la fenêtre principale de Omen. Par défaut : MEDIUM."
+L["Controls the scaling of the main Omen window."] = "Contrôle l'échelle de la fenêtre principale de Omen."
+L["Controls the transparency of the main Omen window."] = "Contrôle la transparence de la fenêtre principale de Omen."
+L["Controls whether the main Omen window can be dragged offscreen"] = "Contrôle si la fenêtre principale de Omen peut être déplacée hors-écran."
+L["Control the font size of the labels"] = "Contrôle la taille de la police des labels."
+L["Control the font size of the title text"] = "Contrôle la taille de la police du texte du titre."
+L["DEATHKNIGHT"] = "Chevalier de la mort"
+L["Disable while tanking"] = "Désactiver en tankant"
+L["DISABLE_WHILE_TANKING_DESC"] = "Ne donne aucun avertissement si vous êtes en posture défensive, en forme d'ours, sous fureur vertueuse ou en présence de givre."
+L["Display large numbers in Ks"] = "Affiche les grands nombres en milliers."
+L["DRUID"] = "Druide"
+L["HERO"] = "Hero"
+L["Enable Screen Flash"] = "Flash de l'écran"
+L["Enable Screen Shake"] = "Secousse de l'écran"
+L["Enable Sound"] = "Son d'avertissement"
+L["Enable Warning Message"] = "Msg d'avertissement"
+L["Fade/MI Bar Color"] = "Couleur A-P Oubli/Img."
+L["FAQ Part 1"] = "FAQ - 1ère partie"
+L["FAQ Part 2"] = "FAQ - 2ème partie"
+L["Font"] = "Police d'écriture"
+L["Font Color"] = "Couleur de la police"
+L["Font Outline"] = "Contour de la police"
+L["Font Size"] = "Taille de la police"
+L["Frame's background color"] = "Couleur de l'arrière-plan du cadre."
+L["Frame's border color"] = "Couleur de la bordure du cadre."
+L["Frame Strata"] = "Couche du cadre"
+L["Frequently Asked Questions"] = "Questions fréquemment posées"
+L["FuBar Options"] = "Options FuBar"
+L["GENERAL_FAQ"] = [=[
+|cffffd200En quoi Omen3 est-il différent de Omen2 ?|r
+
+Omen3 se fie complètement à l'API de la menace de Blizzard ainsi qu'aux évènements de menace. Il n'essaie pas de calculer ou d'extrapoler la menace contrairement à Omen2.
+
+Omen2 utilisait ce que nous avions appelé la bibliothèque Threat-2.0. Cette bibliothèque avait pour mission de surveiller le journal de combat, le lancement des sorts, les buffs, les débuffs, les postures, les talents et les modificateurs de l'équipement afin de calculer la menace de chacun. La menace était calculée en se basant sur ce que l'on connaissait ou approximée selon les comportements observés. De nombreuses compétences telle que Repousser au loin étaient juste assumées (d'être une réduction de 50% de la menace) étant donné qu'il était pratiquement impossible de le vérifier.
+
+La bibliothèque Threat-2.0 comprenait également la partie communication afin de diffuser votre menace aux autres membres du raid, pour autant que ces derniers utilisaient également Threat-2.0. Ces données étaient alors utilisées pour fournir une vue d'ensemble des menaces du raid.
+
+Depuis le patch 3.0.2, Omen ne fait plus rien de cela et n'a plus besoin d'une bibliothèque de menace. Omen3 utilise le nouveau système de surveillance des menaces intégré au jeu par Blizzard pour obtenir les valeurs exactes des menaces de tous les membres. Cela signifie que Omen3 n'a pas besoin de synchroniser les données, d'analyser le journal de combat ou de faire des suppositions, permettant ainsi d'augmenter considérablement les performances au niveau du trafic réseau, du temps CPU et de la mémoire utilisée. L'implémentation de modules de boss pour les événements de menace spécifiques (comme la réinitialisation de la menace lors de l'atterrisage de Plaie-de-nuit) n'est également plus nécessaire.
+
+Un autre avantage de cette nouvelle implementation est l'ajout de la menace des PNJs envers un monstre (ex. la forme humaine de Kalecgos). Cependant, il y a également quelques inconvénients : les fréquences de mise à jour sont beaucoup plus lentes, les détails de la menace ne peuvent être obtenues qu'à condition que quelqu'un dans votre groupe/raid cible le monstre en question et il n'est également pas possible d'obtenir la menace d'un monstre envers lequel vous n'êtes directement en combat.
+
+|cffffd200Comment me débarrasser des 2 lignes verticales du milieu ?|r
+
+Verrouillez votre Omen. Le verrouillage d'Omen empêchera ce dernier d'être déplacé ou redimensionné, et empêche également le redimensionnement des colonnes. Si vous ne l'aviez pas remarqué, les deux lignes verticales grises permettent de redimensionner les colonnes.
+
+|cffffd200Comment faire pour que Omen3 ressemble à Omen2 ?|r
+
+Utilisez comme texture d'arrière-plan et de bordure la texture "Blizzard Tooltip" et mettez la couleur de l'arrière-plan en noir (en mettant la barre de luminosité tout en bas) et celle de la bordure en bleu.
+
+|cffffd200Pourquoi aucune donnée de menace ne s'affiche concernant un monstre que je cible, même s'il est en combat ?|r
+
+L'API de Blizzard ne renvoye pas de données de menace concernant les monstres envers lesquels vous n'êtes pas directement en combat. Nous pensons qu'il s'agit là d'une mesure de Blizzard afin d'économiser la bande passante.
+
+|cffffd200Y-a-t-il un moyen de contourner cette limitation ? Ne pas voir la menace de mon familier est un retour aux suppositions.|r
+
+Il n'y a pas d'autre moyen que de faire la supposition à votre place (ce que faisait Omen2).
+
+Le but d'Omen3 est de fourni des données précises, nous ne voulons plus jouer aux devinettes et du même coup réduire vos IPS. Ayez confiance en votre familier/tank, ou attendez juste 2 secondes avant d'attaquer et utilisez un sort faisant peu de dégâts afin d'avoir une lecture initiale des menaces.
+]=]
+L["GENERAL_FAQ2"] = [=[
+|cffffd200Pouvons-nous avoir à nouveau le mode AoE ?|r
+
+À nouveau, cela n'est pas possible sans deviner les valeurs des menaces. L'API de Blizzard ne permet de récupérer que les données des unités ciblées par quelqu'un dans le raid. Cela veut dire que s'il y a 20 monstres et que seuls 6 sont ciblés, il n'y a aucun moyen d'avoir des données précises concernant les 14 autres.
+
+Il est très difficile de faire des suppositions surtout pour les soins et les buffs (la menace est divisée par le nombre de monstre avec lesquels vous êtes en combatt) car les monstres contrôlés (métamorphose, bannir, etc.) n'ont pas leurs tables des menaces modifiées et les addons ne peuvent déterminer précisément combien de monstres sont en combat avec vous. Les suppositions d'Omen2 étaient pratiquement toujours fausses.
+
+|cffffd200Les bulles d'aide au survol des unités affichent un pourcentage de menace différent de celui de Omen3. Pourquoi ?|r
+
+Le pourcentage de menace de Blizzard est échellonné pour être compris entre 0% et 100% afin que vous repreniez toujours l'aggro à 100%. Omen renvoie les valeurs brutes dont les pourcentages de reprise d'aggro sont de 110% pour ceux en mêlée et de 130% dans les autres cas.
+
+Par convention, la cible principale d'un tank est appelée tank et est définit comme ayant 100% de menace.
+
+|cffffd200Est-ce que Omen3 se synchronise ou analyse le journal de combat ?|r
+
+Non. Omen3 n'essaie pas de se synchroniser ou d'analyser le journal de combat. Il n'y a actuellement aucune intention de le faire.
+
+|cffffd200Les mises à jour de la menace sont lentes...|r
+
+Omen3 met à jour les valeurs des menaces que vous voyez aussi souvent que les mises à jour des valeurs provenants de Blizzard le permettent.
+
+En fait, Blizzard les met à jour toutes les secondes, ce qui est bien plus rapide que la synchro d'Omen2. Avec Omen2, vous transmettiez vos données au reste du raid toutes les 3 sec. (ou 1,5 sec. si tank).
+
+|cffffd200Où puis-je signaler des bogues ou faire une suggestion ?|r
+
+http://forums.wowace.com/showthread.php?t=14249
+
+|cffffd200Qui a écrit Omen3 ?|r
+
+Xinhuan (Blackrock US Alliance).
+
+|cffffd200Acceptez-vous les dons Paypal ?|r
+
+Oui, envoyez-les à xinhuan AROBASE gmail POINT com.
+]=]
+L["General Settings"] = "Paramètres généraux"
+L["Grow bars upwards"] = "Ajouter vers le haut"
+L["Heading background color"] = "La couleur de l'arrière-plan de l'en-tête."
+L["Heading BG Color"] = "Couleur A-P en-tête"
+L["Height of each bar"] = "Hauteur de chaque barre."
+L["Height of the title bar. The minimum height allowed is twice the background border thickness."] = "La hauteur de la barre-titre. La hauteur minimale autorisée est le double de l'épaisseur de la bordure de l'arrière-plan."
+L["Help File"] = "Fichier d'aide"
+L["Hide minimap/FuBar icon"] = "Masquer l'icône de la minicarte/de FuBar"
+L["Hide Omen"] = "Masquer Omen"
+L["Hide Omen entirely if it collapses to show 0 bars"] = "Masque complètement Omen si aucune barre n'est affichée."
+L["Hide Omen on 0 bars"] = "Masquer si 0 barre"
+L["However, hide Omen if any of the following are true (higher priority than the above)."] = "Masque Omen si l'une des suivantes est vraie (priorité supérieure)."
+L["HUNTER"] = "Chasseur"
+L["Ignore Player Pets"] = "Ignorer fam. joueurs"
+L["IGNORE_PLAYER_PETS_DESC"] = [=[Demande à Omen de ne pas s'occuper des familiers des autres joueurs lors de la détermination de l'unité à afficher.
+
+Ces familiers gardent une table des menaces lorsqu'ils sont en mode |cffffff78aggressif|r ou |cffffff78défensif|r et se comportent alors comme les monstres normaux, attaquant la cible avec laquelle ils ont la plus haute menace. Si on leur donne l'ordre d'attaquer une cible spécifique, ils gardent toujours la table des menaces, mais se focalisent sur la cible assignée qui a alors par définition 100% de menace. Ils peuvent être provoqués pour les forcer à vous attaquer.
+
+Les fam. des joueurs en mode |cffffff78passif|r n'ont pas de table des menaces, et la provocation ne fonctionne pas sur eux. Ils attaquent uniquement leur cible assignée et le font sans aucune table des menaces.
+
+Quand un fam. de joueur reçoit l'ordre de |cffffff78suivre|r, la table des menaces du familier est détruite immédiatement et il arrête d'attaquer. Cependant, il risque de reprendre immédiatement une cible s'il se trouve en mode aggressif/défensif.
+]=]
+L["Invert Bar/Text Colors"] = "Inverser couleurs barre/texte"
+L["Left"] = "Gauche"
+L["Lock Omen"] = "Verrouiller Omen"
+L["Locks Omen in place and prevents it from being dragged or resized."] = "Verrouille Omen afin d'empêcher tout déplacement ou redimensionnement de la fenêtre."
+L["MAGE"] = "Mage"
+-- L["Makes the Omen window non-interactive"] = "Makes the Omen window non-interactive"
+L["Max bars to show"] = "Nombre max. de barres"
+L["Max number of bars to show"] = "Détermine le nombre maximal de barres à afficher."
+L["'My Bar' BG Color"] = "Couleur A-P 'Ma barre'"
+L["Name"] = "Nom"
+L["None"] = "Aucun"
+L["*Not in Party*"] = "*Pas dans le groupe*"
+L["OMEN_DESC"] = "Omen est un \"threat meter\" léger qui affiche la menace des monstres avec lesquels vous êtes en combat. Vous pouvez changer l'apparence et le comportement de Omen, et configurer différents profils pour chacun de vos personnages."
+L["Omen Quick Menu"] = "Accès rapide Omen"
+L["OMEN_SLASH_DESC"] = "Ces boutons exécutent exactement les mêmes fonctions que celles de la commande slash /omen."
+L["OMEN_WARNINGS_DESC"] = "Cette section vous permet de personnaliser quand et comment Omen vous prévient si vous êtes sur le point de reprendre l'aggro."
+L["Open Config"] = "Ouvrir fenêtre de config."
+L["Open Omen's configuration panel"] = "Ouvre le panneau de configuration de Omen."
+L["Open the configuration dialog"] = "Ouvre la fenêtre de configuration."
+L["Outline"] = "Contour"
+L["PALADIN"] = "Paladin"
+L["Passed %s%% of %s's threat!"] = "Dépassement des %s%% de la menace |2 %s !"
+L["PET"] = "Familier"
+L["Pet Bar Color"] = "Couleur A-P familiers"
+L["Position"] = "Position"
+L["PRIEST"] = "Prêtre"
+L["Print a message to screen when you accumulate too much threat"] = "Affiche un message à l'écran quand vous avez accumulé trop de menace."
+L["Profiles"] = "Profils"
+L["> Pull Aggro <"] = "> Reprise d'aggro <"
+L["Pull Aggro Bar Color"] = "Couleur A-P reprise"
+L["Right"] = "Droite"
+L["Right-click|r to open the options menu"] = "Clic droit|r pour ouvrir le menu des options."
+L["ROGUE"] = "Voleur"
+L["Scale"] = "Échelle"
+L["Sets how far inside the frame the threat bars will display from the 4 borders of the frame"] = "Définit le décalage des barres de menace par rapport aux 4 côtés du cadre."
+L["SHAMAN"] = "Chaman"
+L["Short Numbers"] = "Nombres courts"
+L["Show a bar for the amount of threat you will need to reach in order to pull aggro."] = "Affiche une barre indiquant la quantité de menace que vous avez besoin de faire pour reprendre l'aggro."
+L["Show bars for these classes"] = "Afficher les barres pour ces classes"
+L["Show Classes..."] = "Afficher les classes…"
+L["SHOW_CLASSES_DESC"] = "Affiche les barres de menace de Omen pour les classes suivantes. Les classes mentionnées ici se réfèrent uniquement aux personnes présentes dans votre groupe/raid, à l'exception de l'option 'Pas dans le groupe'."
+L["Show column headings"] = "Affiche les en-têtes des colonnes."
+L["Show Headings"] = "Afficher les en-têtes"
+L["Show icon"] = "Afficher l'icône"
+L["Show minimap button"] = "Bouton de la minicarte"
+L["Show Omen"] = "Afficher Omen"
+L["Show Omen when..."] = "Afficher Omen quand…"
+L["Show Omen when any of the following are true"] = "Affiche Omen quand l'une des conditions suivantes est vraie."
+L["Show Omen when you are alone"] = "Affiche Omen quand vous êtes tout seul."
+L["Show Omen when you are in a 5-man party"] = "Affiche Omen quand vous êtes dans un groupe de 5 joueurs."
+L["Show Omen when you are in a raid"] = "Affiche Omen quand vous êtes dans un raid."
+L["Show Omen when you have a pet out"] = "Affiche Omen quand un familier est à vos côtés."
+L["Show Pull Aggro Bar"] = "Barre reprise d'aggro"
+L["Show text"] = "Afficher le texte"
+L["Show the Omen minimap button"] = "Affiche le bouton de Omen sur la minicarte."
+L["Show the Omen Title Bar"] = "Affiche la barre-titre de Omen."
+L["Show Threat %"] = "Afficher % de menace"
+L["Show threat per second values"] = "Affiche les valeurs de la menace par seconde."
+L["Show Threat Values"] = "Afficher les valeurs"
+L["Show Title Bar"] = "Afficher la barre-titre"
+L["Show TPS"] = "Afficher la MPS"
+L["Show When..."] = "Afficher quand…"
+L["Slash Command"] = "Commande slash"
+L["Smoothly animate bar changes"] = "Anime de façon fluide les variations des barres."
+L["Sound to play"] = "Son à jouer"
+L["Spacing between each bar"] = "Espacement entre chaque barre."
+L["Switch the colors so that the bar background colors and the text colors are swapped."] = "Permute les couleurs afin que les couleurs de l'arrière-plan des barres et les couleurs du texte soient inversées."
+L["Tank Bar Color"] = "Couleur A-P barre tank"
+L["Tells Omen to additionally check your 'focus' and 'focustarget' before your 'target' and 'targettarget' in that order for threat display."] = "Demande à Omen de vérifier également votre focalisation et la cible de votre focalisation (dans cet ordre) pour l'affichage de la menace."
+L["Tells Omen to enter Test Mode so that you can configure Omen's display much more easily."] = "Demande à Omen d'entrer en mode de test afin que vous puissiez le configurer beaucoup plus facilement."
+L["Test Mode"] = "Mode de test"
+L["Test warnings"] = "Test d'avertissement"
+L["Texture to use for the frame's background"] = "Texture à utiliser pour l'arrière-plan du cadre."
+L["Texture to use for the frame's border"] = "Texture à utiliser pour la bordure du cadre."
+L["The background color for all threat bars"] = "La couleur de l'arrière-plan de toutes les barres de menace."
+L["The background color for pets"] = "La couleur de l'arrière-plan pour les familiers."
+L["The background color for players under the effects of Fade and Mirror Image (they will be at negative 4 million threat)"] = "La couleur de l'arrière-plan pour les joueurs sous l'effet d'Oubli et d'Image du miroir (ils auront une menace négative de 4 million)."
+L["The background color for your Pull Aggro bar"] = "La couleur de l'arrière-plan de votre barre de reprise d'aggro."
+L["The background color for your tank's threat bar"] = "La couleur de l'arrière-plan de la barre de menace du tank."
+L["The background color for your threat bar"] = "La couleur de l'arrière-plan de votre barre de menace."
+L["The color of the labels"] = "La couleur des labels."
+L["The color of the title text"] = "La couleur du texte du titre."
+L["The font that the labels will use"] = "La police d'écriture que les labels utiliseront."
+L["The font that the title text will use"] = "La police d'écriture que le texte du titre utilisera."
+L["The outline that the labels will use"] = "Le contour que les labels utiliseront."
+L["The outline that the title text will use"] = "Le contour que le texte du titre utilisera."
+L["The size used to tile the background texture"] = "La taille de chaque carreau de l'arrière-plan."
+L["The texture that the bar will use"] = "La texture que les barres utiliseront."
+L["The thickness of the border"] = "L'épaisseur de la bordure."
+L["Thick Outline"] = "Contour épais"
+L["This section controls when Omen is automatically shown or hidden."] = "Cette section contrôle quand Omen est automatiquement affiché/caché."
+L["Threat"] = "Menace"
+L["Threat [%]"] = "Menace [%]"
+L["Tile Background"] = "A-P en carreaux"
+L["Tile the background texture"] = "Divise l'arrière-plan en plusieurs carreaux contenant la même texture."
+L["Title Bar Background Options"] = "Options de l'arrière-plan de la barre-titre"
+L["Title Bar Height"] = "Hauteur de la barre-titre"
+L["Title Bar Settings"] = "Param. de la barre-titre"
+L["Title Text Options"] = "Options du texte du titre"
+L["Toggle Focus"] = "Afficher/cacher focal."
+L["Toggle Omen"] = "Afficher/cacher Omen"
+L["TPS"] = "MPS"
+L["TPS Window"] = "Fenêtre MPS"
+L["TPS_WINDOW_DESC"] = "Le calcul de la menace par seconde se fait en temps réel sur une fenêtre des X dernières secondes."
+L["Turning this on will cause Omen to hide whenever you are in a battleground or arena."] = "L'activation de ceci forcera Omen à se masquer quand vous êtes dans un champ de bataille ou en arène."
+L["Turning this on will cause Omen to hide whenever you are in a city or inn."] = "L'activation de ceci forcera Omen à se masquer quand vous êtes dans une capitale ou une auberge."
+L["Turning this on will cause Omen to hide whenever you are not in combat."] = "L'activation de ceci forcera Omen à se masquer si vous n'êtes pas en combat."
+L[""] = ""
+L["Use a different colored background for the tank's threat bar in Omen"] = "Utilise un arrière-plan coloré différent pour la barre de menace du tank dans Omen."
+L["Use a different colored background for your threat bar in Omen"] = "Utilise un arrière-plan coloré différent pour votre barre de menace dans Omen."
+L["Use Auto Show/Hide"] = "Afficher/masquer auto."
+L["Use !ClassColors"] = "Utiliser !ClassColors"
+L["Use Class Colors"] = "Couleurs de classe"
+L["Use !ClassColors addon for class colors for the background color of threat bars"] = "Utilise l'addon !ClassColors pour les couleurs de classe pour l'arrière-plan des barres de menace."
+L["Use Focus Target"] = "Utiliser cible focal."
+L["Use 'My Bar' color"] = "Couleur 'Ma barre'"
+L["Use Same Background"] = "Utiliser le même arrière-plan"
+L["Use standard class colors for the background color of threat bars"] = "Utilise les couleurs de classes standards comme couleur d'arrière-plan des barres de menace."
+L["Use Tank Bar color"] = "Couleur barre tank"
+L["Use the same background settings for the title bar as the main window's background"] = "Utilise les mêmes réglages d'arrière-plan de la fenêtre principale pour la barre-titre."
+L["WARLOCK"] = "Démoniste"
+L["Warning Settings"] = "Param. des avertissements"
+L["Warning Threshold %"] = "Seuil d'avertissement (%)"
+L["Warrior"] = "Guerrier"
+L["WARRIOR"] = "Guerrier"
+L["WARRIOR_FAQ"] = [=[Les données suivantes proviennent de http://www.tankspot.com/forums/f200/39775-wow-3-0-threat-values.html et datent du 2 octobre 2008 (merci à Satrina). Les nombres concernent le niveau 80.
+
+|cffffd200Modificateurs|r
+Posture de combat ____ x 80
+Posture berserk ______ x 80
+Maîtrise tactique ____ x 121/142/163
+Posture défensive ____ x 207.35
+
+Notez que dans nos estimations initiales de la menace (que nous utilisons actuellement dans WoW 2.0), nous égalisions 1 dégât à 1 menace, et utilisions 1.495 pour représenter le multiplicateur de la posture+défi. Nous avons vu que la méthode de Blizzard est d'utiliser le multiplicateur sans décimal, donc dans la version 2.x cela devrait être x149 (peut-être x149.5); et c'est x207 (peut-être 207.3) dans la version 3.0. Il se peut que cela permette le transport de valeurs integer au lieu de valeurs décimales à travers le réseau à des fins d'efficacité. Il semblerait que les valeurs de menace sont multipliées par 207.35 sur le serveur, puis arrondies.
+
+Si vous souhaitez toujours utiliser la méthode 1 dégât = 1 menace, les modificateurs de posture sont 0.8 et 2.0735, etc.
+
+|cffffd200Valeurs de menace (les modificateurs de posture s'appliquent sauf mention contraire) :|r
+Cri de guerre ___________ 78 (divisé)
+Enchaînement ____________ dégâts + 225 (divisé)
+Cri de commandement _____ 80 (divisé)
+Coup traumatisant _______ dégâts uniquement
+Bouclier de dégâts ______ dégâts uniquement
+Cri démoralisant ________ 63 (divisé)
+Dévaster ________________ dégâts + 5% de la PA *** à re-vérifier pour la 8982 **
+Esquive/Parade/Blocage __ 1 (en posture défensive avec Posture défensive améliorée uniquement)
+Frappe héroïque _________ dégâts + 259
+Lancer héroïque _________ 1.50 x dégâts
+Gain de rage ____________ 5 (le modificateur de posture n'est pas appliqué)
+Pourfendre ______________ dégâts uniquement
+Vengeance _______________ dégâts + 121
+Coup de bouclier ________ 36
+Heurt de bouclier _______ dégâts + 770
+Onde de choc ____________ dégâts uniquement
+Heurtoir ________________ dégâts + 140
+Renvoi de sort __________ dégâts uniquement (uniquement les sorts dirigés vers vous)
+Social Aggro ____________ 0
+Fracasser armure ________ 345 + 5% de la PA
+Coup de tonnerre ________ 1.85 x dégâts
+Vigilance _______________ 10% de la menace générée par la cible (le modificateur de posture n'est pas appliqué)
+
+Vous ne gagnez pas de menace pour avoir renvoyé les sorts dirigés vers les alliés avec Renvoi de sort amélioré. Quand vous renvoyez un sort pour un allié, ce dernier gagne la menace générée par les dégâts infligés par le sort renvoyé.
+]=]
+L["You are alone"] = "Vous êtes tout seul"
+L["You are in a battleground"] = "Vous êtes dans un champ de bataille"
+L["You are in a party"] = "Vous êtes en groupe"
+L["You are in a raid"] = "Vous êtes dans un raid"
+L["You are not in combat"] = "Vous n'êtes pas en combat"
+L["You are resting"] = "Vous vous reposez"
+L["You have a pet"] = "Vous avez un familier"
+
diff --git a/Localization/koKR.lua b/Localization/koKR.lua
index c6e45d4..4507700 100644
--- a/Localization/koKR.lua
+++ b/Localization/koKR.lua
@@ -1,10 +1,333 @@
--- THIS CONTENTS OF THIS FILE IS AUTO-GENERATED BY THE WOWACE PACKAGER
--- Please use the Localization App on WoWAce to update this
--- at http://www.wowace.com/projects/omen-threat-meter/localization/
-
--- Korean localization by Sayclub.
-local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
-local L = AceLocale:NewLocale("Omen", "koKR")
-if not L then return end
-
---@localization(locale="koKR", format="lua_additive_table", table-name="L", handle-unlocalized="comment")@
+-- THIS CONTENTS OF THIS FILE IS AUTO-GENERATED BY THE WOWACE PACKAGER
+-- Please use the Localization App on WoWAce to update this
+-- at http://www.wowace.com/projects/omen-threat-meter/localization/
+
+-- Korean localization by Sayclub.
+local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
+local L = AceLocale:NewLocale("Omen", "koKR")
+if not L then return end
+
+L["A collection of help pages"] = "도움말 페이지 목록"
+L["Alpha"] = "투명도"
+L["Always Show Self"] = "항상 자신 표시"
+L["Always show your threat bar on Omen (ignores class filter settings), showing your bar on the last row if necessary"] = "만약 필요하다면 마지막 줄 위에 당신의 바를 보여 주고, 항상 Omen에 당신의 위협 바를 표시합니다. (직업 필터 설정 무시)"
+L["Animate Bars"] = "움직이는 바"
+L["Attach to minimap"] = "미니맵에 표시"
+L["Autocollapse"] = "자동 접기"
+L["Autocollapse Options"] = "자동 접기 옵션"
+L["AUTO_SHOW/HIDE_NOTE"] = "노트: 만약 수동으로 당신이 Omen 표시나 숨기기를 토글 한다면, 다음에 당신이 설정할 때까지 자동 표시/숨김 설정과 관계없이 표시되거나 숨겨집니다. 파티나 공격대에 참여하거나 떠날때 또는 자동 표시/숨김 설정을 변경하셔야 합니다."
+L["Background Color"] = "배경 색상"
+L["Background Options"] = "배경 옵션"
+L["Background Texture"] = "배경 무늬"
+L["Background Tile Size"] = "배경 타일 크기"
+L["Bar BG Color"] = "배경 색상 바"
+L["Bar Height"] = "바 높이"
+L["Bar Inset"] = "바 추가"
+L["Bar Label Options"] = "바 라벨 옵션"
+L["Bar Settings"] = "바 설정"
+L["Bar Spacing"] = "바 간격"
+L["Bar Texture"] = "바 무늬"
+L["Border Color"] = "테두리 색상"
+L["Border Texture"] = "테두리 무늬"
+L["Border Thickness"] = "테두리 두께"
+L["Causes Omen to play a chosen sound effect"] = "선택된 소리를 듣습니다."
+L["Causes the entire game world to shake momentarily. This option only works if nameplates are turned off."] = "전체 게임 화면이 잠시 흔들립니다. 만약 이름표 표시가 켜져 있다면 이 옵션은 작동하지 않습니다."
+L["Causes the entire screen to flash red momentarily"] = "화면 가장자리에 깜박이는 효과를 냅니다."
+L["Center"] = "가운데"
+L["Center Omen"] = "Omen 중앙"
+L["|cffff0000Error:|r Omen cannot use shake warning if you have turned on nameplates at least once since logging in."] = "|cffff0000오류:|r 만약 로그인 후 이름표 표시를 한번이라도 했다면 Omen의 진동 경고를 사용 할 수 없습니다."
+L["Clamp To Screen"] = "화면에 고정"
+L["Click|r to toggle the Omen window"] = "클릭|r Omen 창 토글"
+-- L["Click Through"] = "Click Through"
+L["Collapse to show a minimum number of bars"] = "최소한의 바를 표시하기 위해 접기를 합니다."
+L["Configure"] = "설정"
+L["Configure bar settings."] = "바 세팅을 설정합니다."
+L["Configure title bar settings."] = "제목 바를 설정합니다."
+L["Controls the frame strata of the main Omen window. Default: MEDIUM"] = "Omen 창의 프레임 우선순위를 조절합니다. 기본값: MEDIUM"
+L["Controls the scaling of the main Omen window."] = "Omen 창의 크기를 조절합니다."
+L["Controls the transparency of the main Omen window."] = "Omen 창의 투명도를 조절합니다."
+L["Controls whether the main Omen window can be dragged offscreen"] = "Omen 창 이동시 화면 밖으로 나가지 않도록 조절합니다."
+L["Control the font size of the labels"] = "라벨의 글꼴 크기를 조절합니다."
+L["Control the font size of the title text"] = "제목 문자 글꼴 크기"
+L["DEATHKNIGHT"] = "죽음의 기사"
+L["Disable while tanking"] = "방어전담시 사용 안함"
+L["DISABLE_WHILE_TANKING_DESC"] = "방어 태세, 곰 변신, 정의의 격노나 냉기의 형상일때 모든 경고를 보이지 않습니다."
+L["Display large numbers in Ks"] = "큰 숫자는 Ks로 표시합니다."
+L["DRUID"] = "드루이드"
+L["HERO"] = "Hero"
+L["Enable Screen Flash"] = "화면 깜박임 사용"
+L["Enable Screen Shake"] = "화면 진동 사용"
+L["Enable Sound"] = "소리 사용"
+L["Enable Warning Message"] = "경고 메시지 사용"
+L["Fade/MI Bar Color"] = "소실/환영복제 바 색상"
+L["FAQ Part 1"] = "FAQ Part 1"
+L["FAQ Part 2"] = "FAQ Part 2"
+L["Font"] = "글꼴"
+L["Font Color"] = "글꼴 색상"
+L["Font Outline"] = "글꼴 외곽선"
+L["Font Size"] = "글꼴 크기"
+L["Frame's background color"] = "창의 배경 색상을 설정합니다."
+L["Frame's border color"] = "창의 테두리 색상을 설정합니다."
+L["Frame Strata"] = "프레임 우선순위"
+L["Frequently Asked Questions"] = "질문과 답변"
+L["FuBar Options"] = "FuBar 옵션"
+L["GENERAL_FAQ"] = [=[
+|cffffd200How is Omen3 different from Omen2?|r
+
+Omen3 relies completely on the Blizzard threat API and threat events. It does not attempt to calculate or extrapolate threat unlike Omen2.
+
+Omen2 used what we called the Threat-2.0 library. This library was responsible for monitoring the combat log, spellcasting, buffs, debuffs, stances, talents and gear modifiers for calculating each individuals threat. Threat was calculated based on what was known or approximated from observed behaviors. Many abilities such as knockbacks were just assumed (to be a 50% threat reduction) as they were mostly impossible to confirm.
+
+The Threat-2.0 library also included addon communication to broadcast your threat to the rest of the raid as long as they were also using Threat-2.0. This data was then used to provide a raid wide display of threat information.
+
+Since patch 3.0.2, Omen no longer does any of these things and the need for a threat library is no longer necessary. Omen3 uses Blizzard's new in-built threat monitor to obtain exact values of every members threat. This means Omen3 has no need for synchronisation of data, combat log parsing or guessing, resulting in a significant increase in performance with regards to network traffic, CPU time and memory used. The implementation of boss modules for specific threat events (such as Nightbane wiping threat on landing) are also no longer necessary.
+
+Further benefits of this new implementation include the addition of NPC threat on a mob (eg, Human Kalecgos). However, there are some drawbacks; frequency of updates are much slower, threat details cannot be obtained unless somebody in your party/raid are targetting the mob and it is also not possible to obtain threat from a mob you are not in direct combat with.
+
+|cffffd200How do I get rid of the 2 vertical gray lines down the middle?|r
+
+Lock your Omen. Locking Omen will prevent it from being moved or resized, as well as prevent the columns from being resized. If you haven't realized it, the 2 vertical gray lines are column resizing handles.
+
+|cffffd200How do I make Omen3 look like Omen2?|r
+
+Change the both the Background Texture and Border Texture to Blizzard Tooltip, change the Background Color to black (by dragging the luminance bar to the bottom), and the Border Color to blue.
+
+|cffffd200Why does no threat data show on a mob when I target it even though it is in combat?|r
+
+The Blizzard threat API does not return threat data on any mob you are not in direct combat with. We suspect this is an effort on Blizzard's part to save network traffic.
+
+|cffffd200Is there ANY way around this Blizzard limitation? Not being able to see my pet's threat before I attack has set me back to guessing.|r
+
+There is no way around this limitation short of us doing the guessing for you (which is exactly how Omen2 did it).
+
+The goal of Omen3 is to provide accurate threat data, we no longer intend to guess for you and in the process lower your FPS. Have some confidence in your pet/tank, or just wait 2 seconds before attacking and use a low damage spell such as Ice Lance so that you can get initial threat readings.
+]=]
+L["GENERAL_FAQ2"] = [=[
+|cffffd200Can we get AoE mode back?|r
+
+Again, this is not really possible without guessing threat values. Blizzard's threat API only allows us to query for threat data on units that somebody in the raid is targetting. This means that if there are 20 mobs and only 6 of them are targetted by the raid, there is no way to obtain accurate threat data on the other 14.
+
+This is also extremely complicated to guess particularly for healing and buffing (threat gets divided by the number of mobs you are in combat with) because mobs that are under crowd control effects (sheep, banish, sap, etc) do not have their threat table modified and addons cannot reliably tell how many mobs you are in combat with. Omen2's guess was almost always wrong.
+
+|cffffd200The tooltips on unit mouseover shows a threat % that does not match the threat % reported by Omen3. Why?|r
+
+Blizzard's threat percentage is scaled to between 0% and 100%, so that you will always pull aggro at 100%. Omen reports the raw unscaled values which has pulling aggro percentages at 110% while in melee range and 130% otherwise.
+
+By universal agreement, the primary target of a mob is called the tank and is defined to be at 100% threat.
+
+|cffffd200Does Omen3 sync or parse the combat log?|r
+
+No. Omen3 does not attempt to sync or parse the combat log. Currently there are no intentions to do so.
+
+|cffffd200The threat updates are slow...|r
+
+Omen3 updates the threat values you see as often as Blizzard updates the threat values to us.
+
+In fact, Blizzard updates them about once per second, which is much faster than what Omen2 used to sync updates. In Omen2, you only transmitted your threat to the rest of the raid once every 3 seconds (or 1.5s if you were a tank).
+
+|cffffd200Where can I report bugs or give suggestions?|r
+
+http://forums.wowace.com/showthread.php?t=14249
+
+|cffffd200Who wrote Omen3?|r
+
+Xinhuan (Blackrock US Alliance) did.
+
+|cffffd200Do you accept Paypal donations?|r
+
+Yes, send to xinhuan AT gmail DOT com.
+]=]
+L["General Settings"] = "일반 설정"
+L["Grow bars upwards"] = "바 위로 확장"
+L["Heading background color"] = "제목 배경 색상을 설정합니다."
+L["Heading BG Color"] = "제목 배경 색상"
+L["Height of each bar"] = "각 바의 높이를 설정합니다."
+L["Height of the title bar. The minimum height allowed is twice the background border thickness."] = "제목 바의 높이를 설정합니다. 최소 허락된 높이는 배경 테두리 두께입니다."
+L["Help File"] = "도움말"
+L["Hide minimap/FuBar icon"] = "미니맵/FuBar 아이콘 숨김"
+L["Hide Omen"] = "Omen 숨김"
+L["Hide Omen entirely if it collapses to show 0 bars"] = "바의 갯수가 0개일때 Omen을 숨깁니다."
+L["Hide Omen on 0 bars"] = "바가 0일때 Omen 숨김"
+L["However, hide Omen if any of the following are true (higher priority than the above)."] = "만약 선택한 항목이 사실일때 Omen을 숨깁니다. (최 우선 순위)"
+L["HUNTER"] = "사냥꾼"
+L["Ignore Player Pets"] = "플레이어 소환수들 무시"
+L["IGNORE_PLAYER_PETS_DESC"] = [=[
+Tells Omen to skip enemy player pets when determining which unit to display threat data on.
+
+Player pets maintain a threat table when in |cffffff78Aggressive|r or |cffffff78Defensive|r mode and behave just like normal mobs, attacking the target with the highest threat. If the pet is instructed to attack a specific target, the pet still maintains the threat table, but sticks on the assigned target which by definition has 100% threat. Player pets can be taunted to force them to attack you.
+
+However, player pets on |cffffff78Passive|r mode do not have a threat table, and taunt does not work on them. They only attack their assigned target when instructed and do so without any threat table.
+
+When a player pet is instructed to |cffffff78Follow|r, the pet's threat table is wiped immediately and stops attacking, although it may immediately reacquire a target based on its Aggressive/Defensive mode.
+]=]
+L["Invert Bar/Text Colors"] = "바/문자 색상 반대로"
+L["Left"] = "왼쪽"
+L["Lock Omen"] = "Omen 잠금"
+L["Locks Omen in place and prevents it from being dragged or resized."] = "Omen의 위치나 크기를 조절하지 못하도록 잠급니다."
+L["MAGE"] = "마법사"
+-- L["Makes the Omen window non-interactive"] = "Makes the Omen window non-interactive"
+L["Max bars to show"] = "표시할 최대 바"
+L["Max number of bars to show"] = "표시할 바의 최대 갯수"
+L["'My Bar' BG Color"] = "'플레이어 바'의 배경 색상"
+L["Name"] = "이름"
+L["None"] = "없음"
+L["*Not in Party*"] = "*파티에 속하지 않음*"
+L["OMEN_DESC"] = "Omen은 당신이 전투중인 몹들에 대한 당신의 위협 수준을 보여주는 가벼운 위협 미터기 입니다."
+L["Omen Quick Menu"] = "Omen 빠른 메뉴"
+L["OMEN_SLASH_DESC"] = "슬래시 명령어인 /omen을 입력하면 이 버튼과 같은 기능을 수행하는 명령어를 표시합니다."
+L["OMEN_WARNINGS_DESC"] = "이 섹션은 당신이 어그로를 얻을때 언제, 어떻게 Omen이 당신에게 경고할지를 설정합니다."
+L["Open Config"] = "설정 열기"
+L["Open Omen's configuration panel"] = "Omen 설정 패널 열기"
+L["Open the configuration dialog"] = "설정 창을 엽니다."
+L["Outline"] = "얇은 외곽선"
+L["PALADIN"] = "성기사"
+L["Passed %s%% of %s's threat!"] = "%2$s 위협 수준의 %1$s%% 초과함!"
+L["PET"] = "소환수"
+L["Pet Bar Color"] = "소환수 바 색상"
+L["Position"] = "위치"
+L["PRIEST"] = "사제"
+L["Print a message to screen when you accumulate too much threat"] = "너무 많은 위협 수준이 쌓일때 화면에 메시지를 보여줍니다."
+L["Profiles"] = "프로필"
+L["> Pull Aggro <"] = "> 어그로 획득 <"
+L["Pull Aggro Bar Color"] = "어그로 획득 바 색상"
+L["Right"] = "오른쪽"
+L["Right-click|r to open the options menu"] = "우-클릭|r 설정 메뉴 열기"
+L["ROGUE"] = "도적"
+L["Scale"] = "크기"
+L["Sets how far inside the frame the threat bars will display from the 4 borders of the frame"] = "창 안에 보여질 위협 바의 4 테두리를 설정합니다."
+L["SHAMAN"] = "주술사"
+L["Short Numbers"] = "간결한 숫자"
+L["Show a bar for the amount of threat you will need to reach in order to pull aggro."] = "어그로를 획득하기 위해 당신이 넘어야 할 위협 바를 표시합니다."
+L["Show bars for these classes"] = "이 직업들을 위한 바를 표시합니다."
+L["Show Classes..."] = "직업 표시..."
+L["SHOW_CLASSES_DESC"] = "다음 직업들을 위해 Omen 위협 바를 표시합니다. 여기 직업은 '파티에 속하지 않음' 옵션을 제외하고 당신이 파티/공격대일때 표시합니다."
+L["Show column headings"] = "컬럼 제목 표시"
+L["Show Headings"] = "제목 표시"
+L["Show icon"] = "아이콘 표시"
+L["Show minimap button"] = "미니맵 버튼 표시"
+L["Show Omen"] = "Omen 표시"
+L["Show Omen when..."] = "Omen을 언제 표시할지..."
+L["Show Omen when any of the following are true"] = "선택한 항목에 따라 Omen을 표시합니다."
+L["Show Omen when you are alone"] = "솔로잉일 경우에 Omen을 표시합니다."
+L["Show Omen when you are in a 5-man party"] = "5인 파티일 경우에 Omen을 표시합니다."
+L["Show Omen when you are in a raid"] = "공격대일 경우에 Omen을 표시합니다."
+L["Show Omen when you have a pet out"] = "소환수를 가진 경우에 Omen을 표시합니다"
+L["Show Pull Aggro Bar"] = "어그로 획득 바 표시"
+L["Show text"] = "텍스트 표시"
+L["Show the Omen minimap button"] = "Omen 미니맵 버튼을 표시합니다."
+L["Show the Omen Title Bar"] = "Omen 제목 바를 표시합니다."
+L["Show Threat %"] = "% 수치 표시"
+L["Show threat per second values"] = "초당 위협 수준을 표시합니다."
+L["Show Threat Values"] = "위협 수치 표시"
+L["Show Title Bar"] = "제목 바 표시"
+L["Show TPS"] = "TPS 표시"
+L["Show When..."] = "표시 방법..."
+L["Slash Command"] = "슬래시 명령어"
+L["Smoothly animate bar changes"] = "바가 부드럽게 움직이도록 변경합니다."
+L["Sound to play"] = "재생할 소리"
+L["Spacing between each bar"] = "각 바 사이의 간격을 설정합니다."
+L["Switch the colors so that the bar background colors and the text colors are swapped."] = "바 배경 색상과 문자 색상이 변경 되도록 색상을 변경합니다."
+L["Tank Bar Color"] = "방어전담 바 색상"
+L["Tells Omen to additionally check your 'focus' and 'focustarget' before your 'target' and 'targettarget' in that order for threat display."] = "Omen에 당신의 '대상'과 '대상의 대상'에 대한 위협을 표시하기 위해 추가로 당신의 '주시'와 '주시 대상'을 검사합니다."
+L["Tells Omen to enter Test Mode so that you can configure Omen's display much more easily."] = "Omen을 쉽게 설정할 수 있도록 테스트 모드에 들어갑니다."
+L["Test Mode"] = "테스트 모드"
+L["Test warnings"] = "테스트 경고"
+L["Texture to use for the frame's background"] = "창의 배경에 사용할 무늬를 설정합니다."
+L["Texture to use for the frame's border"] = "창의 테두리에 사용할 무늬를 설정합니다."
+L["The background color for all threat bars"] = "모든 위협 바들에 대한 배경 색상을 지정합니다."
+L["The background color for pets"] = "소환수에 대한 배경 색상을 지정합니다."
+L["The background color for players under the effects of Fade and Mirror Image (they will be at negative 4 million threat)"] = "소실과 환영 복제 (이것은 4 백만의 위협을 감소함) 효과의 플레이어에 대한 배경 색상을 지정합니다."
+L["The background color for your Pull Aggro bar"] = "당신의 어그로 획득 바에 대한 배경 색상을 지정합니다."
+L["The background color for your tank's threat bar"] = "당신의 방어전담(탱커) 위협 바에 대한 배경 색상을 지정합니다."
+L["The background color for your threat bar"] = "플레이어(당신)의 바에 대한 배경 색상을 지정합니다."
+L["The color of the labels"] = "라벨의 색상을 설정합니다."
+L["The color of the title text"] = "제목 문자 색상"
+L["The font that the labels will use"] = "라벨에 사용할 글꼴을 설정합니다."
+L["The font that the title text will use"] = "제목 문자 사용 글꼴"
+L["The outline that the labels will use"] = "라벨에 사용할 외곽선을 설정합니다."
+L["The outline that the title text will use"] = "제목 문자 사용 외곽선"
+L["The size used to tile the background texture"] = "배경 무늬의 타일 크기를 설정합니다."
+L["The texture that the bar will use"] = "바에 사용할 무늬을 설정합니다."
+L["The thickness of the border"] = "테두리의 두께를 설정합니다."
+L["Thick Outline"] = "두꺼운 외곽선"
+L["This section controls when Omen is automatically shown or hidden."] = "자동적으로 Omen을 표시하거나 숨기기 위해 이 섹션을 설정합니다."
+L["Threat"] = "위협 수준"
+L["Threat [%]"] = "위협 수준 [%]"
+L["Tile Background"] = "타일 배경"
+L["Tile the background texture"] = "배경의 무늬에 사용할 타일을 설정합니다."
+L["Title Bar Background Options"] = "제목 바 배경 옵션"
+L["Title Bar Height"] = "제목 바 높이"
+L["Title Bar Settings"] = "제목 바 설정"
+L["Title Text Options"] = "제목 문자 옵션"
+L["Toggle Focus"] = "주시 대상 켜기/끄기"
+L["Toggle Omen"] = "Omen 열기/닫기"
+L["TPS"] = "TPS"
+L["TPS Window"] = "TPS 창"
+L["TPS_WINDOW_DESC"] = "초당 위협 수준 계산을 X 초 마다 실시간으로 창에 표시합니다."
+L["Turning this on will cause Omen to hide whenever you are in a battleground or arena."] = "당신이 전장이나 투기장에 있을때 Omen을 숨깁니다."
+L["Turning this on will cause Omen to hide whenever you are in a city or inn."] = "당신이 대도시나 여관에 있을때 Omen을 숨깁니다."
+L["Turning this on will cause Omen to hide whenever you are not in combat."] = "당신이 전투 중이 아닐때 Omen을 숨깁니다."
+L[""] = "<무엇인가>"
+L["Use a different colored background for the tank's threat bar in Omen"] = "Omen에서 방어전담(탱커)의 위협 바에 다른 색상을 사용합니다."
+L["Use a different colored background for your threat bar in Omen"] = "Omen에서 플레이어(당신)의 위협 바에 다른 색상을 사용합니다. "
+L["Use Auto Show/Hide"] = "자동 표시/숨김 사용"
+L["Use !ClassColors"] = "!ClassColors 사용"
+L["Use Class Colors"] = "직업 색상 사용"
+L["Use !ClassColors addon for class colors for the background color of threat bars"] = "위협 바의 배경 색상을 위해 !ClassColors 애드온을 사용합니다."
+L["Use Focus Target"] = "주시 대상 사용"
+L["Use 'My Bar' color"] = "'플레이어 바'에 색상 사용"
+L["Use Same Background"] = "같은 배경 사용"
+L["Use standard class colors for the background color of threat bars"] = "위협 바의 배경 색상을 기본 직업 색상으로 사용합니다."
+L["Use Tank Bar color"] = "방어전담 바 색상 사용"
+L["Use the same background settings for the title bar as the main window's background"] = "메인 창의 배경으로 제목 바와 같은 배경 설정을 사용합니다."
+L["WARLOCK"] = "흑마법사"
+L["Warning Settings"] = "경고 설정"
+L["Warning Threshold %"] = "%값에 경고"
+L["Warrior"] = "전사"
+L["WARRIOR"] = "전사"
+L["WARRIOR_FAQ"] = [=[The following data is obtained from |cffffd200http://www.tankspot.com/forums/f200/39775-wow-3-0-threat-values.html|r on 2nd Oct 2008 (credits to Satrina). The numbers are for a level 80.
+
+|cffffd200Modifiers|r
+Battle Stance ________ x 80
+Berserker Stance _____ x 80
+Tactical Mastery _____ x 121/142/163
+Defensive Stance _____ x 207.35
+
+Note that in our original threat estimations (that we use now in WoW 2.0), we equated 1 damage to 1 threat, and used 1.495 to represent the stance+defiance multiplier. We see that Blizzard's method is to use the multiplier without decimals, so in 2.x it would've been x149 (maybe x149.5); it is x207 (maybe 207.3) in 3.0. I expect that this is to allow the transport of integer values instead of decimal values across the Internet for efficiency. It appears that threat values are multiplied by 207.35 at the server, then rounded.
+
+If you still want to use the 1 damage = 1 threat method, the stance modifiers are 0.8 and 2.0735, etc.
+
+|cffffd200Threat Values (stance modifiers apply unless otherwise noted):|r
+Battle Shout _________ 78 (split)
+Cleave _______________ damage + 225 (split)
+Commanding Shout _____ 80 (split)
+Concussion Blow ______ damage only
+Damage Shield ________ damage only
+Demoralising Shout ___ 63 (split)
+Devastate ____________ damage + 5% of AP *** Needs re-checking for 8982 **
+Dodge/Parry/Block_____ 1 (in defensive stance with Improved Defensive Stance only)
+Heroic Strike ________ damage + 259
+Heroic Throw _________ 1.50 x damage
+Rage Gain ____________ 5 (stance modifier is not applied)
+Rend _________________ damage only
+Revenge ______________ damage + 121
+Shield Bash __________ 36
+Shield Slam __________ damage + 770
+Shockwave ____________ damage only
+Slam _________________ damage + 140
+Spell Reflect ________ damage only (only for spells aimed at you)
+Social Aggro _________ 0
+Sunder Armour ________ 345 + 5%AP
+Thunder Clap _________ 1.85 x damage
+Vigilance ____________ 10% of target's generated threat (stance modifier is not applied)
+
+You do not gain threat for reflecting spells targetted at allies with Improved Spell Reflect. When you reflect a spell for an ally, your ally gains the threat for the damage dealt by the reflected spell.
+]=]
+L["You are alone"] = "솔로잉시"
+L["You are in a battleground"] = "전장 참여"
+L["You are in a party"] = "파티 참여"
+L["You are in a raid"] = "공격대 참여"
+L["You are not in combat"] = "비 전투시"
+L["You are resting"] = "휴식 상태시"
+L["You have a pet"] = "소환수를 가졌을때"
+
diff --git a/Localization/ruRU.lua b/Localization/ruRU.lua
index 508ea15..63d859e 100644
--- a/Localization/ruRU.lua
+++ b/Localization/ruRU.lua
@@ -1,10 +1,328 @@
--- THIS CONTENTS OF THIS FILE IS AUTO-GENERATED BY THE WOWACE PACKAGER
--- Please use the Localization App on WoWAce to update this
--- at http://www.wowace.com/projects/omen-threat-meter/localization/
-
--- Russian localization file for ruRU by StingerSoft.
-local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
-local L = AceLocale:NewLocale("Omen", "ruRU")
-if not L then return end
-
---@localization(locale="ruRU", format="lua_additive_table", table-name="L", handle-unlocalized="comment")@
+-- THIS CONTENTS OF THIS FILE IS AUTO-GENERATED BY THE WOWACE PACKAGER
+-- Please use the Localization App on WoWAce to update this
+-- at http://www.wowace.com/projects/omen-threat-meter/localization/
+
+-- Russian localization file for ruRU by StingerSoft.
+local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
+local L = AceLocale:NewLocale("Omen", "ruRU")
+if not L then return end
+
+L["A collection of help pages"] = "Коллекция страниц помощи"
+L["Alpha"] = "Прозрачность"
+L["Always Show Self"] = "Всегда показывать себя"
+L["Always show your threat bar on Omen (ignores class filter settings), showing your bar on the last row if necessary"] = "Всегда отображать вашу полосу угрозы в Omen (игнорируя настройки фильтрации по классу), отображая её в последнем ряду, если это необходимо"
+L["Animate Bars"] = "Анимация полос"
+L["Attach to minimap"] = "Закрепить у мини-карты"
+L["Autocollapse"] = "Автосворачивание"
+L["Autocollapse Options"] = "Настройки автосворачивания"
+L["AUTO_SHOW/HIDE_NOTE"] = "Заметка: Если вы отобразили/скрыли окно Omen вручную, он будет оставаться скрытым или будет отображаться вне зависимости от настроек автоматического отображения/скрытия, пока вы не смените зону, не вступите/покинете группу/рейд, или не смените настроек автоматического отображения/скрытия."
+L["Background Color"] = "Цвет фона"
+L["Background Options"] = "Опции фона"
+L["Background Texture"] = "Текстура фона"
+L["Background Tile Size"] = "Размер фонового узора"
+L["Bar BG Color"] = "Цвет фона полос"
+L["Bar Height"] = "Высота полос"
+L["Bar Inset"] = "Врезка полос"
+L["Bar Label Options"] = "Настройка надписей полос"
+L["Bar Settings"] = "Настройка полос"
+L["Bar Spacing"] = "Промежуток между полосами"
+L["Bar Texture"] = "Текстура полос"
+L["Border Color"] = "Цвет краев"
+L["Border Texture"] = "Текстура краев"
+L["Border Thickness"] = "Толщина краев"
+L["Causes Omen to play a chosen sound effect"] = "Проигрывать выбранный вами звуковой эффект"
+L["Causes the entire game world to shake momentarily. This option only works if nameplates are turned off."] = "Весь экран будет трястись. Данная настройка будет работать только если выключены таблицы имен"
+L["Causes the entire screen to flash red momentarily"] = "Весь экран будет мигать красным цветом"
+L["Center"] = "По центру"
+L["Center Omen"] = "Центрировать Omen"
+L["|cffff0000Error:|r Omen cannot use shake warning if you have turned on nameplates at least once since logging in."] = "|cffff0000Ошибка:|r Omen не может использовать предупреждения встряской, если вы включали nameplates после входа в игру."
+L["Clamp To Screen"] = "Прикрепить к экрану"
+L["Click|r to toggle the Omen window"] = "Клик|r для открытия/закрытия окна Omen"
+-- L["Click Through"] = "Click Through"
+L["Collapse to show a minimum number of bars"] = "Сжимать окно для отображения минимального количества полос"
+L["Configure"] = "Настройка"
+L["Configure bar settings."] = "Настройка полос."
+L["Configure title bar settings."] = "Настройка заглавной полосы."
+L["Controls the frame strata of the main Omen window. Default: MEDIUM"] = "Контроль слоя фрейма главного окна Омена. По умолчанию: Среднее"
+L["Controls the scaling of the main Omen window."] = "Настройка масштаба главного окна Omen."
+L["Controls the transparency of the main Omen window."] = "Настройка прозрачности главного окна Omen."
+L["Controls whether the main Omen window can be dragged offscreen"] = "Контролирует возможность выноса главного окна Omen за пределы экрана"
+L["Control the font size of the labels"] = "Настройка размера шрифта надписей"
+L["Control the font size of the title text"] = "Настройка размера шрифта заглавного текста"
+L["DEATHKNIGHT"] = "Рыцарь смерти"
+L["Disable while tanking"] = "Отключать при танковании"
+L["DISABLE_WHILE_TANKING_DESC"] = "Не выводить предупреждений, если на вас активна Оборонительная стойка, Облик медведя, Праведное неистовство или Власть льда."
+L["Display large numbers in Ks"] = "Отображать большие числа с сокращением К"
+L["DRUID"] = "Друид"
+L["HERO"] = "Hero"
+L["Enable Screen Flash"] = "Включить мигание экрана"
+L["Enable Screen Shake"] = "Включить встряску экрана"
+L["Enable Sound"] = "Включить звуки"
+L["Enable Warning Message"] = "Включить предупреждения"
+L["Fade/MI Bar Color"] = "Цвет полос УвТ/ЗИ"
+L["FAQ Part 1"] = "FAQ, часть 1"
+L["FAQ Part 2"] = "FAQ, часть 2"
+L["Font"] = "Шрифт"
+L["Font Color"] = "Цвет шрифта"
+L["Font Outline"] = "Контур шрифта"
+L["Font Size"] = "Размер шрифта"
+L["Frame's background color"] = "Цвет фона фрейма"
+L["Frame's border color"] = "Цвет краев фрейма"
+L["Frame Strata"] = "Cлои фрейма"
+L["Frequently Asked Questions"] = "Часто задаваемые вопросы"
+L["FuBar Options"] = "Опции FuBar"
+L["GENERAL_FAQ"] = [=[|cffffd200Чем Omen3 отличается от Omen2?|r
+
+Omen3 берёт информацию из стандартного API угрозы и событий угрозы. Он не вычисляет или экстраполирует угрозу в отличие от Omen2.
+
+Omen2 использует библиотеку Threat-2.0. Эта библиотека отвечает за мониторинг журнала боя, применение заклинаний, баффов, дебаффов, стоек, талантов и комплектацию снаряжения для вычисления угрозы индивидуально для каждого. Вычисление угрозы основано на известном или приблизительно известном наблюдаемом поведении. Значения уменьшения угрозы многих способностей, таких, как, например, Отпор лишь приблизительно, так как точные значения, в большинстве своём, не удаётся определить.
+
+Библиотека Threat-2.0 также включала аддон связи, передававший значение вашей угрозы остальным участникам рейда, если они также использовали Threat-2.0. Эти данные использовались для обеспечения рейда информацией о угрозе.
+
+После патча 3.0.2 Omen больше не делает чего-либо из этих вещей, и необходимости в библиотеке Threat-2.0 больше нет. Omen3 использует новый, встроенный в стандартный интерфейс игры мониторинг угрозы, для получения точных значений угрозы каждого игрока. Это означает, что Omen3 больше не нуждается в синхронизации данных, мониторинга журнала боя, что привело к значительному увеличению эффективности с точки зрения использования сетевого трафика, CPU и памяти. Необходимость в реализации специальных событий боссов, связанных с обнулением угрозы (к примеру, как Ночная Погибель при посадке обнуляет всю угрозу) также отпадает.
+
+Другие преимущества новой реализации включают в себя добавление угрозы NPC на мобе (например, Калесгос в форме человека). Однако, присутствуют и некоторые недостатки: частота обновлений намного медленнее, сведения об угрозе не могут быть получены, пока кто-либо из вашей группы/рейда не выделит моба, а также невозможно получить сведения об угрозе мобу, с которым вы не находитесь в бою.
+
+|cffffd200Как избавиться от двух вертикальных серых линий?|r
+
+Зафиксируйте Omen. Фиксация Omen не позволит перетаскивать его, или изменять его размер, а также не позволит изменять размер колонок. Если вы не сделали этого, две вертикальные серые линии - это регулировщики изменения размера колонок.
+
+|cffffd200Как сделать Omen3 похожим на Omen2?|r
+
+Смените Текстуру фона и Текстуру краев на Blizzard Tooltip, Цвет фона на черный (с помощью перетаскивания полосы яркости вниз), а Цвет краев - на синий.
+
+|cffffd200Почему данные об угрозе не отображаются, когда я выделяю моба, даже если он находится в режиме боя?|r
+
+Стандартный API угрозы не возвращает данные об угрозе мобам, с которыми вы не находитесь в непосредственном бою. Мы подозреваем, что это является попыткой со стороны Blizzard сохранить сетевой трафик.
+
+|cffffd200Существует ли какой-либо способ обойти это ограничение Blizzard? Невозможность увидеть угрозу своего питомца, пока я не атакую сам, заставляет меня только строить предположения об этом.|r
+
+Нет способа обойти это ограничение, это мог сделать только Omen2.
+
+Цель Omen3 - обеспечивать точной информацией об угрозе, мы не намерены делать что-либо, снижающее FPS. Надейтесь на вашего питомца/танка, или просто подождите 2 секунды перед тем, как атаковать, и используйте заклинание с небольшим уроном типа Ледяного копья, это поможет вам получить начальные данные об угрозе.
+]=]
+L["GENERAL_FAQ2"] = [=[|cffffd200Можно ли вернуть АоЕ-режим?|r
+
+Повторюсь, это невозможно без получения приблизительных значений угрозы. Стандартный API угрозы позволяет нам запрашивать данные об угрозе только тем мобам, которые являются целью кого-либо из участников вашего рейда. Это означает, что если вы находитесь в бою с 20 мобами, но только 6 из них являются целями участников рейда, нет способа получить точную информацию об угрозе другим 14.
+
+Также очень сложно определять значения угрозы от лечения и баффов (угроза от которых распределяется на всех мобов, с которыми вы находитесь в бою), так как таблица угрозы мобов не модифицируется при нахождении под эффектами контроля (Превращение, Изгнание, Ошеломление и т.п.) , а аддоны не могут предоставить вам достоверную информацию о количестве мобов, с котоыми вы находитесь в бою. Предположения Omen2 почти всегда были неверны.
+
+|cffffd200Всплывающие подсказки, отображающиеся при наведении курсора на моба, отображают % угрозы, не соответствующий отображаемому в Omen3. Почему?|r
+
+Стандартные значения угрозы изменяются в диапазоне от 0% до 100%, то есть, вы всегда будете срывать аггро при 100%. Данные Omen являются приблизительными значениями, а процент срыва аггро является 110% для находящихся в мили-радиусе, и 130% в других случаях.
+
+По всеобщему соглашению, основная цель моба называется танком и имеет 100% угрозы.
+
+|cffffd200Берет ли Omen3 информацию из журнала боя/синхронизируется ли с ним?|r
+
+Нет. Omen3 не делает этого. В данный момент нет намерений делать что-либо подобное.
+
+|cffffd200Данные об угрозе обновляются медленно...|r
+
+Omen3 обновляет значения угрозы, которые вы видите, с такой же частотой, с которой Blizzard обновляет значения угрозы для нас.
+
+Фактически, Blizzard обновляет их раз в секунду, что намного быстрее, чем синхронизация Omen2. Omen2 отправлял остальным участникам рейда ваше значение угрозы каждые 3 секунды (или 1.5с, если вы были танком).
+
+|cffffd200Куда я могу сообщать о багах или давать предложения?|r
+
+http://forums.wowace.com/showthread.php?t=14249
+
+|cffffd200Кто написал Omen3?|r
+
+Это сделал Xinhuan (Blackrock US, Альянс).
+
+|cffffd200Принимаете ли вы пожертвования посредством Paypal?|r
+
+Да, отправляйте их на xinhuan AT gmail DOT com.]=]
+L["General Settings"] = "Основные настройки"
+L["Grow bars upwards"] = "Возрастание полос вверх"
+L["Heading background color"] = "Цвет фона заголовка"
+L["Heading BG Color"] = "Цвет фона заголовка"
+L["Height of each bar"] = "Высота всех полос"
+L["Height of the title bar. The minimum height allowed is twice the background border thickness."] = "Высота полосы заглавия. Минимальная высота в два раза превышает толщину краев фона."
+L["Help File"] = "Файл справки"
+L["Hide minimap/FuBar icon"] = "Скрыть иконку у мини-карты/FuBar'а"
+L["Hide Omen"] = "Скрыть Omen"
+L["Hide Omen entirely if it collapses to show 0 bars"] = "Полностью скрывать Omen, когда он сжимается до 0 полос"
+L["Hide Omen on 0 bars"] = "Скрывать Omen при отсутствии полос"
+L["However, hide Omen if any of the following are true (higher priority than the above)."] = "Тем не менее скрывать Omen, если какое-либо из следующих условий верно (приоритет выше, чем у вышеперечисленного)."
+L["HUNTER"] = "Охотник"
+L["Ignore Player Pets"] = "Игнор. питомцев"
+L["IGNORE_PLAYER_PETS_DESC"] = [=[Игнорировать питомцев вражеских игроков при определении объекта для отображения данных по угрозе.
+
+Питомцы игрока отображаются в таблице угрозы, в |cffffff78Агрессивном|r или |cffffff78Оборонительном|r режиме, и ведут себя, как обычные мобы, атакуя цель с наибольшим уровнем угрозы. Если питомцу указано атаковать конкретные цели, питомец будет поддерживать угрозу в таблице, полоса установленной цели по определению будет указывать 100% угрозы. Питомцы игрока могут быть спровоцированы, чтобы заставить их атаковать вас.
+
+Однако, питомцы в |cffffff78пассивном|r режиме не будут внесены с таблицу угрозы, и провокация на них не подействует. Они будут атаковать только указанные цели, и не будут внесены в таблицу угрозы.
+
+Если питомцу приказано |cffffff78Следовать|r, то значения в таблице угрозы питомца будут аннулированы, а атака прекратится, но, несмотря на это, он может снова приобрести цель в зависимости от того, находится он в режиме Агрессии, или же Обороны.
+]=]
+L["Invert Bar/Text Colors"] = "Инвертировать цвета полос/текста"
+L["Left"] = "Слева"
+L["Lock Omen"] = "Зафиксировать Omen"
+L["Locks Omen in place and prevents it from being dragged or resized."] = "Фиксирует Omen на месте, не позволяя изменять размер или перетаскивать его."
+L["MAGE"] = "Маг"
+-- L["Makes the Omen window non-interactive"] = "Makes the Omen window non-interactive"
+L["Max bars to show"] = "Макс. полос"
+L["Max number of bars to show"] = "Максимальное число показываемых полос"
+L["'My Bar' BG Color"] = "Цвет фона 'Моей полосы'"
+L["Name"] = "Имя"
+L["None"] = "Отсутствует"
+L["*Not in Party*"] = "*Не в группе*"
+L["OMEN_DESC"] = "Omen - это легковесный измеритель угрозы, который показывает вам угрозу по отношению к монстрам, с которыми вы вступили в бой. Вы можете изменить его вид, параметры работы, а также сконфигурировать различные профили для каждого из ваших персонажей."
+L["Omen Quick Menu"] = "Быстрое меню Omen"
+L["OMEN_SLASH_DESC"] = "Эти кнопки выполняют те же функции, что и слеш-команды /omen"
+L["OMEN_WARNINGS_DESC"] = "Данный раздел поможет вам настроить вывод сообщений о том, что вы почти перехватили агрессию моба на себя."
+L["Open Config"] = "Открыть настройки"
+L["Open Omen's configuration panel"] = "Открыть окно настроек Omen"
+L["Open the configuration dialog"] = "Открыть окно настройки"
+L["Outline"] = "Контур"
+L["PALADIN"] = "Паладин"
+L["Passed %s%% of %s's threat!"] = "Выработано %s%% от угрозы |3-1(%s)!"
+L["PET"] = "Питомец"
+L["Pet Bar Color"] = "Цвет полос питомцев"
+L["Position"] = "Позиция"
+L["PRIEST"] = "Жрец"
+L["Print a message to screen when you accumulate too much threat"] = "Выводить сообщение на экран, когда вы вырабатываете слишком много угрозы"
+L["Profiles"] = "Профили"
+L["> Pull Aggro <"] = "> Срыв аггро <"
+L["Pull Aggro Bar Color"] = "Цвет полосы срыва аггро"
+L["Right"] = "Справа"
+L["Right-click|r to open the options menu"] = "Клик правой кнопкой|r для открытия меню настроек"
+L["ROGUE"] = "Разбойник"
+L["Scale"] = "Масштаб"
+L["Sets how far inside the frame the threat bars will display from the 4 borders of the frame"] = "Установить глубину нахождения полос угрозы во фрейме по отношению к его краям"
+L["SHAMAN"] = "Шаман"
+L["Short Numbers"] = "Сокращать цифры"
+L["Show a bar for the amount of threat you will need to reach in order to pull aggro."] = "Отображать полосу со значением угрозы, достигнув которого вы сорвёте аггро."
+L["Show bars for these classes"] = "Показывать полосы для этих классов"
+L["Show Classes..."] = "Показывать классы..."
+L["SHOW_CLASSES_DESC"] = "Показывать полосы угрозы Омена для определённых классов. Относится только к группе/рейду. Исключением является опция 'Не в группе'."
+L["Show column headings"] = "Отображать заголовки колонок"
+L["Show Headings"] = "Показ заголовков"
+L["Show icon"] = "Отображать иконку"
+L["Show minimap button"] = "Кнопка у мини-карты"
+L["Show Omen"] = "Показать Omen"
+L["Show Omen when..."] = "Отображать Omen, когда..."
+L["Show Omen when any of the following are true"] = "Показывать Omen, если какое-либо из следующих условий верно"
+L["Show Omen when you are alone"] = "Отображать Omen, когда вы один"
+L["Show Omen when you are in a 5-man party"] = "Отображать Omen, когда вы находитесь в группе"
+L["Show Omen when you are in a raid"] = "Отображать Omen, когда вы находитесь в рейде"
+L["Show Omen when you have a pet out"] = "Отображать Omen, когда у вас есть питомец"
+L["Show Pull Aggro Bar"] = "Отображать полосу срыва аггро"
+L["Show text"] = "Отображать текст"
+L["Show the Omen minimap button"] = "Отображать кнопку Omen у мини-карты"
+L["Show the Omen Title Bar"] = "Отображать заглавную полосу Omen"
+L["Show Threat %"] = "Отображение угрозы в %"
+L["Show threat per second values"] = "Отображать значение вырабатываемой в секунду угрозы"
+L["Show Threat Values"] = "Отображать значения угрозы"
+L["Show Title Bar"] = "Отображать полосу заглавия"
+L["Show TPS"] = "Отображать УгрВС"
+L["Show When..."] = "Показывать, когда..."
+L["Slash Command"] = "Команды"
+L["Smoothly animate bar changes"] = "Плавная анимация изменений полос"
+L["Sound to play"] = "Проигрываемый звук"
+L["Spacing between each bar"] = "Промежутки между полосами"
+L["Switch the colors so that the bar background colors and the text colors are swapped."] = "Поменять местами цвета фона и текста."
+L["Tank Bar Color"] = "Цвет полосы танка"
+L["Tells Omen to additionally check your 'focus' and 'focustarget' before your 'target' and 'targettarget' in that order for threat display."] = "Выполнять дополнительную проверку вашего 'focus' перед проверкой 'target' и 'targettarget' на наличие угрозы для ее отображения."
+L["Tells Omen to enter Test Mode so that you can configure Omen's display much more easily."] = "Войти в режим тестирования для более удобной настройки Omen."
+L["Test Mode"] = "Режим тестирования"
+L["Test warnings"] = "Тестирование предупреждений"
+L["Texture to use for the frame's background"] = "Текстура фона фрейма"
+L["Texture to use for the frame's border"] = "Текстура краев фрейма"
+L["The background color for all threat bars"] = "Цвет фона для всех полос угрозы"
+L["The background color for pets"] = "Цвет фона питомцев"
+L["The background color for players under the effects of Fade and Mirror Image (they will be at negative 4 million threat)"] = "Цвет фона для игроков, находящихся под эффектами Уход в тень и Зеркальное изображение (угроза -4 млн.)"
+L["The background color for your Pull Aggro bar"] = "Цвет фона полосы срыва аггро"
+L["The background color for your tank's threat bar"] = "Цвет фона полосы угрозы вашего танка"
+L["The background color for your threat bar"] = "Цвет фона вашей полосы угрозы"
+L["The color of the labels"] = "Цвет надписей"
+L["The color of the title text"] = "Цвет заглавного текста"
+L["The font that the labels will use"] = "Шрифт, используемый для надписей"
+L["The font that the title text will use"] = "Шрифт, используемый в заглавии"
+L["The outline that the labels will use"] = "Контур шрифта надписей"
+L["The outline that the title text will use"] = "Контур заглавного текста"
+L["The size used to tile the background texture"] = "Размер узора, наносимого на текстуру фона"
+L["The texture that the bar will use"] = "Текстура, используемая для полос"
+L["The thickness of the border"] = "Толщина краев"
+L["Thick Outline"] = "Толстый контур"
+L["This section controls when Omen is automatically shown or hidden."] = "Данный раздел отвечает за автоматическое скрытие или отображение окна Omen."
+L["Threat"] = "Угроза"
+L["Threat [%]"] = "Угроза [%]"
+L["Tile Background"] = "Узорчатый фон"
+L["Tile the background texture"] = "Нанесение узора на текстуру фона"
+L["Title Bar Background Options"] = "Опции фона заглавной полосы"
+L["Title Bar Height"] = "Высота заглавной полосы"
+L["Title Bar Settings"] = "Настройки заглавной полосы"
+L["Title Text Options"] = "Опции заглавного текста"
+L["Toggle Focus"] = "Вкл/откл фокус"
+L["Toggle Omen"] = "Показать/Скрыть Omen"
+L["TPS"] = "УгрВС"
+L["TPS Window"] = "Окно УгрВС"
+L["TPS_WINDOW_DESC"] = "Вычисление угрозы в секунду базируется на количестве угрозы, выработанной за последние Х секунд."
+L["Turning this on will cause Omen to hide whenever you are in a battleground or arena."] = "Скрывать Omen, когда вы находитесь на арене или поле боя."
+L["Turning this on will cause Omen to hide whenever you are in a city or inn."] = "Скрывать Omen, когда вы находитесь в городе или таверне."
+L["Turning this on will cause Omen to hide whenever you are not in combat."] = "Скрывать Omen вне боя."
+L[""] = "<Неизвестно>"
+L["Use a different colored background for the tank's threat bar in Omen"] = "Использовать другую окраску фона полос угрозы танков в Omen"
+L["Use a different colored background for your threat bar in Omen"] = "Использовать другую окраску фона для вашей полосы угрозы в Omen"
+L["Use Auto Show/Hide"] = "Автоматическое отображение/скрытие"
+L["Use !ClassColors"] = "Исп. !ClassColors"
+L["Use Class Colors"] = "Использовать цвет класса"
+L["Use !ClassColors addon for class colors for the background color of threat bars"] = "Использовать аддон !ClassColors для цвета классов и цвета фона полос угрозы"
+L["Use Focus Target"] = "Использовать фокус"
+L["Use 'My Bar' color"] = "Использовать цвет 'Моей полосы'"
+L["Use Same Background"] = "Одинаковый фон"
+L["Use standard class colors for the background color of threat bars"] = "Использовать стандартный цвет класса в качестве цвета фона полосы угрозы"
+L["Use Tank Bar color"] = "Использовать цвет полосы танка"
+L["Use the same background settings for the title bar as the main window's background"] = "Использовать для заглавной полосы такие же настройки фона, что и для основного окна"
+L["WARLOCK"] = "Чернокнижник"
+L["Warning Settings"] = "Настройки предупреждений"
+L["Warning Threshold %"] = "Порог предупреждения в %"
+L["Warrior"] = "Воин"
+L["WARRIOR"] = "Воин"
+L["WARRIOR_FAQ"] = [=[Следующие данные взяты с |cffffd200http://www.tankspot.com/forums/f200/39775-wow-3-0-threat-values.html|r 2 Окт 2008 (автор - Satrina). Значения для 80 уровня.
+
+|cffffd200Модификатор|r
+Боевая стойка ____________ x 80
+Стойка берсерка ___________x 80
+Тактическое превосходство _x 121/142/163
+Оборонительная стойка _____x 207.35
+
+Note that in our original threat estimations (that we use now in WoW 2.0), we equated 1 damage to 1 threat, and used 1.495 to represent the stance+defiance multiplier. We see that Blizzard's method is to use the multiplier without decimals, so in 2.x it would've been x149 (maybe x149.5); it is x207 (maybe 207.3) in 3.0. I expect that this is to allow the transport of integer values instead of decimal values across the Internet for efficiency. It appears that threat values are multiplied by 207.35 at the server, then rounded.
+
+Если Вы все же хотите использовать метод, когда 1 единица урона = 1 единице угрозы, модификатор стоек - 0.8 и 2.0735.
+
+|cffffd200Значения угрозы (stance modifiers apply unless otherwise noted):|r
+Боевой крик __________ 78 (split)
+Рассекающий удар _____ урон + 225 (split)
+Командирский крик ____ 80 (split)
+Оглушающий удар ______ только урон
+Ранящий щит __________ только урон
+Деморализующий крик __ 63 (split)
+Сокрушение ___________ урон + 5% от СА *** Нужно перепроверить для 8982 **
+Уклон/Парир/Блок______ 1 (только в оборонительной стойке с улучшенной оборонительной стойкой)
+Удар героя ___________ урон + 259
+Героический бросок ____1.50 x урона
+Получение Ярости______ 5 (не модифицируется стойкой)
+Кровопускание_________ только урон
+Реванш ______________ урон + 121
+Удар щитом ___________ 36
+Мощный удар щитом _____урон + 770
+Ударная волна _________только урон
+Сокрушение ____________урон + 140
+Отражение заклинания __только урон (только для заклинаний, направленных на вас)
+Общественное аггро ___ 0
+Раскол брони _________ 345 + 5%СА
+Удар грома ___________ 1.85 x урона
+Бдительность __________10% от вырабатываемой угрозы цели (не модифицируется стойкой)
+
+Вы не вырабатываете угрозы при отражении заклинаний, направленных на союзников, с помощью Улучшенного отражения заклинаний. Если вы отражаете заклинание, направленное на союзника, ваш союзник вырабатывает угрозу за нанесенный отраженным заклинанием урон.]=]
+L["You are alone"] = "Вы один"
+L["You are in a battleground"] = "Вы на поле боя"
+L["You are in a party"] = "Вы в группе"
+L["You are in a raid"] = "Вы в рейде"
+L["You are not in combat"] = "Вы не в бою"
+L["You are resting"] = "Вы отдыхаете"
+L["You have a pet"] = "У вас есть питомец"
+
diff --git a/Localization/zhCN.lua b/Localization/zhCN.lua
index 5dfac75..159738e 100644
--- a/Localization/zhCN.lua
+++ b/Localization/zhCN.lua
@@ -1,10 +1,333 @@
--- THIS CONTENTS OF THIS FILE IS AUTO-GENERATED BY THE WOWACE PACKAGER
--- Please use the Localization App on WoWAce to update this
--- at http://www.wowace.com/projects/omen-threat-meter/localization/
-
--- zhCN localization by Ananhaid.
-local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
-local L = AceLocale:NewLocale("Omen", "zhCN")
-if not L then return end
-
---@localization(locale="zhCN", format="lua_additive_table", table-name="L", handle-unlocalized="comment")@
+-- THIS CONTENTS OF THIS FILE IS AUTO-GENERATED BY THE WOWACE PACKAGER
+-- Please use the Localization App on WoWAce to update this
+-- at http://www.wowace.com/projects/omen-threat-meter/localization/
+
+-- zhCN localization by Ananhaid.
+local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
+local L = AceLocale:NewLocale("Omen", "zhCN")
+if not L then return end
+
+L["A collection of help pages"] = "收集的帮助页"
+L["Alpha"] = "透明度"
+L["Always Show Self"] = "总是显示自己"
+L["Always show your threat bar on Omen (ignores class filter settings), showing your bar on the last row if necessary"] = "总是显示你的 Omen 威胁计量条(忽略职业过滤设置),如需要显示你的计量条位于最后一排。"
+L["Animate Bars"] = "动态计量条"
+L["Attach to minimap"] = "依附到小地图"
+L["Autocollapse"] = "自动收起"
+L["Autocollapse Options"] = "自动收起选项"
+L["AUTO_SHOW/HIDE_NOTE"] = "注意:如果您手动切换 Omen 显示或隐藏,将忽略自动显示/隐藏设置、加入或退出队伍或团队和更改任何自动显示/隐藏设置,切换区域之前它仍将显示或隐藏。"
+L["Background Color"] = "背景颜色"
+L["Background Options"] = "背景选项"
+L["Background Texture"] = "背景材质"
+L["Background Tile Size"] = "标题背景尺寸"
+L["Bar BG Color"] = "计量条背景颜色"
+L["Bar Height"] = "计量条高度"
+L["Bar Inset"] = "嵌入计量条"
+L["Bar Label Options"] = "计量条标签选项"
+L["Bar Settings"] = "计量条设置"
+L["Bar Spacing"] = "计量条间距"
+L["Bar Texture"] = "计量条材质"
+L["Border Color"] = "边框颜色"
+L["Border Texture"] = "边框材质"
+L["Border Thickness"] = "边框厚度"
+L["Causes Omen to play a chosen sound effect"] = "成因时 Omen 播放选择的音效"
+L["Causes the entire game world to shake momentarily. This option only works if nameplates are turned off."] = "成因时整个游戏屏幕震动。此选项只有在不启用姓名栏时才可用。"
+L["Causes the entire screen to flash red momentarily"] = "成因时整个屏幕闪动红色"
+L["Center"] = "居中"
+L["Center Omen"] = "Omen 居中"
+L["|cffff0000Error:|r Omen cannot use shake warning if you have turned on nameplates at least once since logging in."] = "|cffff0000错误:|r 如果您在登录后启动过一次姓名版,Omen 将无法使用震动警报功能。"
+L["Clamp To Screen"] = "屏幕锁定"
+L["Click|r to toggle the Omen window"] = "点击|r开/关 Omen 窗口"
+-- L["Click Through"] = "Click Through"
+L["Collapse to show a minimum number of bars"] = "收起以显示最小数量的计量条"
+L["Configure"] = "配置"
+L["Configure bar settings."] = "配置计量条设置。"
+L["Configure title bar settings."] = "配置标题栏设置"
+L["Controls the frame strata of the main Omen window. Default: MEDIUM"] = "控制 Omen 主要窗口的框架阶层。默认:MEDIUM(中)"
+L["Controls the scaling of the main Omen window."] = "控制 Omen 主窗口的尺寸。"
+L["Controls the transparency of the main Omen window."] = "控制 Omen 主窗口的透明度。"
+L["Controls whether the main Omen window can be dragged offscreen"] = "控制 Omen 主要窗口是否可以被拖动到屏幕外。"
+L["Control the font size of the labels"] = "标签字体大小"
+L["Control the font size of the title text"] = "标题文本字体大小"
+L["DEATHKNIGHT"] = "死亡骑士"
+L["Disable while tanking"] = "当为 Tank 时关闭警报"
+L["DISABLE_WHILE_TANKING_DESC"] = "如果在防御姿态、熊形态、正义之怒与冰霜系时、不显示警报。"
+L["Display large numbers in Ks"] = "大数字时用千位(K)显示"
+L["DRUID"] = "德鲁伊"
+L["HERO"] = "Hero"
+L["Enable Screen Flash"] = "开启屏幕闪动"
+L["Enable Screen Shake"] = "开启屏幕震动"
+L["Enable Sound"] = "开启音效"
+L["Enable Warning Message"] = "开启警报消息"
+L["Fade/MI Bar Color"] = "渐隐/镜像计量条颜色"
+L["FAQ Part 1"] = "FAQ 第1部分"
+L["FAQ Part 2"] = "FAQ 第2部分"
+L["Font"] = "字体"
+L["Font Color"] = "字体颜色"
+L["Font Outline"] = "字体轮廓"
+L["Font Size"] = "字体大小"
+L["Frame's background color"] = "框体背景颜色"
+L["Frame's border color"] = "框体边框颜色"
+L["Frame Strata"] = "框架阶层"
+L["Frequently Asked Questions"] = "常见问题"
+L["FuBar Options"] = "FuBar 选项"
+L["GENERAL_FAQ"] = [=[
+|cffffd200Omen3 与 Omen2 的区别|r
+
+Omen3 依据暴雪所提供的仇恨接口,与 Omen2 不一样,Omen3 不会去预测或者计算仇恨,仇恨值是通过接口直接向服务器端获取。
+
+Omen2 使用 Threat-2.0 库,这个库是根据检测的战斗记录、法术、增益与减益、姿态和装备来计算不同的仇恨值。仇恨的计算是根据现有的资料与检测所得到的数据计算的。很多技能是假设值,无法验证(如击退、我们假设击退仇恨降低50%)。
+
+Threat 2.0库也包含了其他人都用这个库的时候同步整个团队的威胁值,这些威胁数据用来提供给整个团队以供参考。
+
+从3.0.2补丁开始 Omen 将不再做这些动作,Threat 库不再需要。Omen3 所使用暴雪内建威胁监视器,并从中获取威胁资料,这也造成 Omen 不再需要同步资料、检测战斗记录与猜测数据。效果因减少资料的传送而提升,包括 CPU 与内存的使用量都会减少,针对特殊首领的威胁变化事件也不再需要。
+
+更进一步说受益包括一些 NPC 对怪物的威胁,比如太阳之井高地人类形态的卡雷苟斯的威胁。但是也有一些不利的地方,威胁数据更新也变得慢了,团队中如果没有人的目标是那个 NPC 的话,他的威胁也无法得到,而你没有直接参与的战斗你也无法获得威胁(如你没有造成任何伤害,或者制造任何威胁你将无法得到威胁值,就算你让你的宠物上去攻击而你没有攻击也一样不能得到威胁值)。
+
+|cffffd200我如何去除中间2条灰色垂直线?|r
+
+锁定 Omen。锁定 Omen 将防止它被移动或调整大小,以及防止栏被调整。如果你没有注意到它,调整两条灰色垂直线。
+
+|cffffd200如何将 Omen3 的外观改成与 Omen2 类似?|r
+
+改变背景材质与边框材质为 Blizzard Tooltip,将背景颜色改成黑色(通过拖动亮度条调节底部颜色),边框颜色改为蓝色。
+
+|cffffd200为什么就算我再战斗中也看不到任何威胁值?|r
+
+除非你对怪有作出任何伤害或者造成任何威胁,否则暴雪威胁接口不会给出任何威胁值,我们猜测这可能是暴雪为了减少网络资料的传送。
+
+|cffffd200有没有办法解决暴雪的这个的限制?攻击之前无法看到我的宠物的威胁。|r
+
+我们没有办法在短期内解决这个限制(正如 Omen2 没有一样)。
+
+Omen3 的目标是提供准确的威胁数据,我们不再打算推测你的数据并降低你的 FPS。你需信任你的宠物/Tank,或者攻击之前等待2秒并用低伤害法术(如冰枪)攻击,这样可以让你可以得到初步的威胁读数。
+]=]
+L["GENERAL_FAQ2"] = [=[
+|cffffd200我们能让 AoE 模块回来吗?|r
+
+同样,这是不是真的有可能不猜测威胁值。暴雪的威胁接口仅限我们查询某人的威胁数据于团队目标。这意味着假如20个怪中只有6个被团队选定,那么就没有办法获得其他14个怪的威胁数据。
+
+这也是极其复杂的猜测治疗和增益效果(得到威胁依靠你在与怪战斗)因为怪处于控制效果(变形术、放逐、闷棍)并没有他们的仇恨列表并且插件不能准确的告知正在与多少个怪战斗。Omen2 猜测的几乎总是错误的。
+
+|cffffd200鼠标悬停单位显示了威胁百分比与 Omen3 的仇恨百分比报告并不一致,为什么?|r
+
+暴雪的威胁比例调整为0%至100%,这样你总是在100%是获得仇恨。Omen 报告的没有调整过,获得仇恨百分比为110%,或混战为130%。
+
+普遍认为,怪的主要目标被称为 Tank 并被定义为获得100%的威胁。
+
+|cffffd200Omen3 有同步资料或者分析战斗资料么?|r
+
+Omen3 不需要同步资料或者分析战斗资料,现阶段没有任何必要去这样做。
+
+|cffffd200威胁资料更新太慢……|r
+
+Omen3 威胁值更新速度与暴雪提供威胁数据给我们的速度是一样的。
+
+事实上,暴雪每秒更新它们一次,这个速度大大超过了 Omen2 的同步频率。在 Omen2,你只是每3秒(Tank 每1.5秒)交换一次威胁数据。
+
+|cffffd200我要去哪里反馈 Bug 或者提出建议?|r
+
+http://forums.wowace.com/showthread.php?t=14249
+
+|cffffd200谁制作了 Omen3?|r
+
+Xinhuan (Blackrock US Alliance) 美服-黑石山-联盟:Xinhuan。
+
+|cffffd200是否接受 Paypal(贝宝)捐助?|r
+
+是的,发送到『xinhuan AT gmail DOT com』。
+]=]
+L["General Settings"] = "综合设置"
+L["Grow bars upwards"] = "计量条向上生长"
+L["Heading background color"] = "主标题的背景颜色"
+L["Heading BG Color"] = "主标题的背景颜色"
+L["Height of each bar"] = "每个计量条的高度"
+L["Height of the title bar. The minimum height allowed is twice the background border thickness."] = "标题栏高度。最小厚度为背景边框厚度的一倍。"
+L["Help File"] = "帮助文件"
+L["Hide minimap/FuBar icon"] = "隐藏小地图/FuBar 小图标"
+L["Hide Omen"] = "隐藏 Omen"
+L["Hide Omen entirely if it collapses to show 0 bars"] = "当没有计量条时收起隐藏 Omen"
+L["Hide Omen on 0 bars"] = "当没有计量条时隐藏 Omen"
+L["However, hide Omen if any of the following are true (higher priority than the above)."] = "但选定其他项时 Omen 将被隐藏(比以上项更具优先级)。"
+L["HUNTER"] = "猎人"
+L["Ignore Player Pets"] = "忽略玩家宠物"
+L["IGNORE_PLAYER_PETS_DESC"] = [=[
+让 Omen 忽略敌对玩家宠物以确定显示哪些单位的威胁。
+
+玩家宠物|cffffff78攻击|r或者|cffffff78防御|r状态保持威胁与正常的怪物相同,正被攻击目标具有最高的威胁。如果宠物指定攻击一个具体目标,宠物仍然保持在威胁列表,但保持在指定的目标定义100%威胁之上。可以玩家宠物嘲讽以攻击你。
+
+然而,玩家宠物在|cffffff78被动|r模式并没有威胁列表,嘲讽依然不起作用。它们只攻击指定的目标和指令时没有仇恨列表。
+
+当玩家宠物处于|cffffff78跟随|r状态时,宠物的威胁列表被消除并立刻停止攻击,虽然它可能会立即重新指定目标位于攻击/防御模式。
+]=]
+L["Invert Bar/Text Colors"] = "互换计量条/文本颜色"
+L["Left"] = "左边"
+L["Lock Omen"] = "锁定 Omen"
+L["Locks Omen in place and prevents it from being dragged or resized."] = "锁定 Omen,使其无法移动或拉伸。"
+L["MAGE"] = "法师"
+-- L["Makes the Omen window non-interactive"] = "Makes the Omen window non-interactive"
+L["Max bars to show"] = "计量条的显示数量"
+L["Max number of bars to show"] = "计量条最大显示数量"
+L["'My Bar' BG Color"] = "“My Bar”背景颜色"
+L["Name"] = "名字"
+L["None"] = "无"
+L["*Not in Party*"] = "*不在队伍中*"
+L["OMEN_DESC"] = "Omen 是一个占用少量系统资源监控威胁的 UI,可以显示你和同伴参与战斗中怪物的威胁列表。你可以改变 Omen 的外观,并且根据不同的角色储存不同的设置。"
+L["Omen Quick Menu"] = "Omen 快捷菜单"
+L["OMEN_SLASH_DESC"] = "这些按钮与斜杠命令有相同的效果(/omen)"
+L["OMEN_WARNINGS_DESC"] = "这里可以调整 Omen 何时用何种方式给你提出仇恨警报。"
+L["Open Config"] = "打开配置"
+L["Open Omen's configuration panel"] = "打开 Omen 配置面板"
+L["Open the configuration dialog"] = "打开 Omen 配置窗口"
+L["Outline"] = "轮廓"
+L["PALADIN"] = "圣骑士"
+L["Passed %s%% of %s's threat!"] = "已超过>%2$s<的%1$2.0f%%威胁!"
+L["PET"] = "宠物"
+L["Pet Bar Color"] = "宠物计量条颜色"
+L["Position"] = "位置"
+L["PRIEST"] = "牧师"
+L["Print a message to screen when you accumulate too much threat"] = "当你仇恨过高时在屏幕上显示警报消息"
+L["Profiles"] = "档案"
+L["> Pull Aggro <"] = ">获得仇恨<"
+L["Pull Aggro Bar Color"] = "获得仇恨计量条颜色"
+L["Right"] = "右边"
+L["Right-click|r to open the options menu"] = "右键|r打开选项菜单"
+L["ROGUE"] = "潜行者"
+L["Scale"] = "缩放"
+L["Sets how far inside the frame the threat bars will display from the 4 borders of the frame"] = "威胁计量条显示的内框与外框之间距离"
+L["SHAMAN"] = "萨满祭司"
+L["Short Numbers"] = "简化数字"
+L["Show a bar for the amount of threat you will need to reach in order to pull aggro."] = "显示一个数值威胁计量条,以帮助获得仇恨。"
+L["Show bars for these classes"] = "显示的职业条"
+L["Show Classes..."] = "显示职业"
+L["SHOW_CLASSES_DESC"] = "Omen 将显示以下职业的威胁。除非你点选“不在队伍中”的选项,否则 Omen 将只显示你队伍中的玩家。"
+L["Show column headings"] = "显示主标题栏"
+L["Show Headings"] = "显示标题"
+L["Show icon"] = "显示小图标"
+L["Show minimap button"] = "显示小地图图标"
+L["Show Omen"] = "显示 Omen"
+L["Show Omen when..."] = "当何时显示 Omen"
+L["Show Omen when any of the following are true"] = "当符合以下条件时 Omen 会被显示"
+L["Show Omen when you are alone"] = "当你没有队伍时显示 Omen"
+L["Show Omen when you are in a 5-man party"] = "当你在5人队伍中显示 Omen"
+L["Show Omen when you are in a raid"] = "当你在团队中显示 Omen"
+L["Show Omen when you have a pet out"] = "当你的宠物存在时显示 Omen"
+L["Show Pull Aggro Bar"] = "显示获得仇恨计量条"
+L["Show text"] = "显示文字"
+L["Show the Omen minimap button"] = "显示 Omen 小地图图标"
+L["Show the Omen Title Bar"] = "显示 Omen 标题栏"
+L["Show Threat %"] = "显示威胁值%"
+L["Show threat per second values"] = "显示每秒威胁值"
+L["Show Threat Values"] = "显示威胁值"
+L["Show Title Bar"] = "显示标题栏"
+L["Show TPS"] = "显示 TPS"
+L["Show When..."] = "当何时显示"
+L["Slash Command"] = "斜杠命令"
+L["Smoothly animate bar changes"] = "平滑动态计量条"
+L["Sound to play"] = "播放音效"
+L["Spacing between each bar"] = "计量条之间的距离"
+L["Switch the colors so that the bar background colors and the text colors are swapped."] = "将计量条背景颜色与文本颜色进行互换。"
+L["Tank Bar Color"] = "Tank 计量条颜色"
+L["Tells Omen to additionally check your 'focus' and 'focustarget' before your 'target' and 'targettarget' in that order for threat display."] = "让 Omen 额外检查您的“焦点”和“焦点目标”位于“目标”和“目标的目标”之前的顺序显示威胁。"
+L["Tells Omen to enter Test Mode so that you can configure Omen's display much more easily."] = "让 Omen 进入测试模式,你可以更容易的配置 Omen 显示。"
+L["Test Mode"] = "测试模式"
+L["Test warnings"] = "测试警报"
+L["Texture to use for the frame's background"] = "框体背景材质"
+L["Texture to use for the frame's border"] = "框体边框材质"
+L["The background color for all threat bars"] = "所有威胁计量条背景颜色"
+L["The background color for pets"] = "宠物背景颜色"
+L["The background color for players under the effects of Fade and Mirror Image (they will be at negative 4 million threat)"] = "受到渐隐或镜像效果影响的玩家背景颜色(他们将减少4百万威胁)"
+L["The background color for your Pull Aggro bar"] = "获得仇恨计量条背景颜色"
+L["The background color for your tank's threat bar"] = "Tank 威胁计量条背景颜色"
+L["The background color for your threat bar"] = "威胁计量条的背景颜色"
+L["The color of the labels"] = "标签颜色"
+L["The color of the title text"] = "标题字体颜色"
+L["The font that the labels will use"] = "标签字体"
+L["The font that the title text will use"] = "标题文本字体"
+L["The outline that the labels will use"] = "标签轮廓"
+L["The outline that the title text will use"] = "标题文本字体轮廓"
+L["The size used to tile the background texture"] = "标题背景材质尺寸"
+L["The texture that the bar will use"] = "计量条材质"
+L["The thickness of the border"] = "边框厚度"
+L["Thick Outline"] = "加倍轮廓"
+L["This section controls when Omen is automatically shown or hidden."] = "这些控制 Omen 何时自动显示或隐藏。"
+L["Threat"] = "威胁"
+L["Threat [%]"] = "威胁[%]"
+L["Tile Background"] = "标题背景"
+L["Tile the background texture"] = "标题背景材质"
+L["Title Bar Background Options"] = "标题栏背景选项"
+L["Title Bar Height"] = "标题栏高度"
+L["Title Bar Settings"] = "标题栏设置"
+L["Title Text Options"] = "标题文本选项"
+L["Toggle Focus"] = "开/关焦点"
+L["Toggle Omen"] = "开/关 Omen"
+L["TPS"] = "TPS"
+L["TPS Window"] = "TPS 窗口"
+L["TPS_WINDOW_DESC"] = "每秒威胁值的计算是根据最后X秒窗口内的变化而决定的。"
+L["Turning this on will cause Omen to hide whenever you are in a battleground or arena."] = "当你位于战场或竞技场时,启用此项会使 Omen 隐藏。"
+L["Turning this on will cause Omen to hide whenever you are in a city or inn."] = "当你位于城市或者旅店时,启用此项会使 Omen 隐藏。"
+L["Turning this on will cause Omen to hide whenever you are not in combat."] = "如选择此项,离开战斗后 Omen 会被隐藏。"
+L[""] = "<未知的>"
+L["Use a different colored background for the tank's threat bar in Omen"] = "为 Omen Tank 威胁计量条使用不同背景颜色。"
+L["Use a different colored background for your threat bar in Omen"] = "在 Omen 中为威胁计量条使用不同的背景颜色"
+L["Use Auto Show/Hide"] = "使用自动显示/隐藏"
+L["Use !ClassColors"] = "使用 !ClassColors"
+L["Use Class Colors"] = "使用职业颜色"
+L["Use !ClassColors addon for class colors for the background color of threat bars"] = "使用 !ClassColors 职业颜色为威胁条背景颜色。"
+L["Use Focus Target"] = "使用焦点目标"
+L["Use 'My Bar' color"] = "使用“My Bar”颜色"
+L["Use Same Background"] = "使用相同背景"
+L["Use standard class colors for the background color of threat bars"] = "使用标准职业颜色威胁计量条背景颜色"
+L["Use Tank Bar color"] = "使用 Tank 计量条颜色"
+L["Use the same background settings for the title bar as the main window's background"] = "使用相同背景设置为主窗口标题栏背景"
+L["WARLOCK"] = "术士"
+L["Warning Settings"] = "警报设置"
+L["Warning Threshold %"] = "警告阈值(百分比)"
+L["Warrior"] = "战士"
+L["WARRIOR"] = "战士"
+L["WARRIOR_FAQ"] = [=[以下数据来自|cffffd200http://www.tankspot.com/forums/f200/39775-wow-3-0-threat-values.html|r2008年10月2号(Satrina 的功劳)。等级80采集的数据。
+
+|cffffd200状态|r
+战斗姿态 ________ x 80
+狂暴姿态 _____ x 80
+战术掌握 _____ x 121/142/163
+防御姿态 _____ x 207.35
+
+请注意,在我们原来估计的威胁(使用魔兽世界2.0),我们把1伤害等同1威胁,并因姿态和挑衅乘以1.495。我们看到暴雪罪的做法未乘以小数点后面的数字,所以在2.x中,将已经x149(或者x149.5);这是x207(或者207.3)在3.0版本。我想这是允许的传输整数值不是小数值在互联网上的效率。看来,威胁值乘以207.35在服务器上,然后四舍五入。
+
+如果您仍希望使用1伤害=1威胁的方法,姿态为是0.8和2.0735,等等。
+
+|cffffd200威胁值(姿态适用,除非另有说明):|r
+战斗怒吼 _________ 78 (split)
+顺劈斩 _______________ damage + 225 (split)
+命令怒吼 _____ 80 (split)
+震荡猛击 ______ damage only
+伤害反射护盾 ________ damage only
+挫志怒吼 ___ 63 (split)
+毁灭打击 ____________ damage + 5% of AP *** Needs re-checking for 8982 **
+躲闪/招架/格挡_____ 1 (in defensive stance with Improved Defensive Stance only)
+英勇打击 ________ damage + 259
+英勇投掷 _________ 1.50 x damage
+怒气获得 ____________ 5 (stance modifier is not applied)
+撕裂 _________________ damage only
+复仇 ______________ damage + 121
+盾击 __________ 36
+盾牌猛击 __________ damage + 770
+震荡波 ____________ damage only
+猛击 _________________ damage + 140
+法术反射 ________ damage only (only for spells aimed at you)
+社会仇恨 _________ 0
+破甲攻击 ________ 345 + 5%AP
+雷霆一击 _________ 1.85 x damage
+警戒 ____________ 10% of target's generated threat (stance modifier is not applied)
+
+如果你通过加强反弹魔法天赋帮助队友反弹魔法,你不会获得任何仇恨值,相对如果你通过此天赋帮助队友反弹魔法,这个仇恨值将计算在队友身上。
+]=]
+L["You are alone"] = "无队伍"
+L["You are in a battleground"] = "在战场"
+L["You are in a party"] = "有队伍"
+L["You are in a raid"] = "有团队"
+L["You are not in combat"] = "你不在战斗"
+L["You are resting"] = "有空闲"
+L["You have a pet"] = "有宠物"
+
diff --git a/Localization/zhTW.lua b/Localization/zhTW.lua
index 45cbd28..b7a7b08 100644
--- a/Localization/zhTW.lua
+++ b/Localization/zhTW.lua
@@ -1,10 +1,330 @@
--- THIS CONTENTS OF THIS FILE IS AUTO-GENERATED BY THE WOWACE PACKAGER
--- Please use the Localization App on WoWAce to update this
--- at http://www.wowace.com/projects/omen-threat-meter/localization/
-
--- zhTW localization
-local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
-local L = AceLocale:NewLocale("Omen", "zhTW")
-if not L then return end
-
---@localization(locale="zhTW", format="lua_additive_table", table-name="L", handle-unlocalized="comment")@
+-- THIS CONTENTS OF THIS FILE IS AUTO-GENERATED BY THE WOWACE PACKAGER
+-- Please use the Localization App on WoWAce to update this
+-- at http://www.wowace.com/projects/omen-threat-meter/localization/
+
+-- zhTW localization
+local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
+local L = AceLocale:NewLocale("Omen", "zhTW")
+if not L then return end
+
+L["A collection of help pages"] = "幫助頁匯集"
+L["Alpha"] = "透明度"
+L["Always Show Self"] = "總是顯示自己"
+L["Always show your threat bar on Omen (ignores class filter settings), showing your bar on the last row if necessary"] = "總是顯示你的 Omen 威脅棒條(忽略職業過濾設定),如需要顯示你的棒條位於最後一排."
+L["Animate Bars"] = "動態棒條"
+L["Attach to minimap"] = "依附到小地圖"
+L["Autocollapse"] = "自動收起"
+L["Autocollapse Options"] = "自動收起選項"
+L["AUTO_SHOW/HIDE_NOTE"] = "注意:如果您手動切換 Omen 顯示或隱藏,將忽略自動顯示/隱藏設定,加入或退出隊伍或團隊和更改任何自動顯示/隱藏設定,切換區域之前它仍將顯示或隱藏."
+L["Background Color"] = "背景顏色"
+L["Background Options"] = "背景選項"
+L["Background Texture"] = "背景材質"
+L["Background Tile Size"] = "標題背景尺寸"
+L["Bar BG Color"] = "棒條背景顏色"
+L["Bar Height"] = "棒條高度"
+L["Bar Inset"] = "嵌入棒條"
+L["Bar Label Options"] = "棒條標籤選項"
+L["Bar Settings"] = "棒條設定"
+L["Bar Spacing"] = "棒條間距"
+L["Bar Texture"] = "棒條材質"
+L["Border Color"] = "邊框顏色"
+L["Border Texture"] = "邊框材質"
+L["Border Thickness"] = "邊框厚度"
+L["Causes Omen to play a chosen sound effect"] = "令 Omen 播放所選擇的音效"
+L["Causes the entire game world to shake momentarily. This option only works if nameplates are turned off."] = "令整個遊戲螢幕震動.此選項只有在不啟用名牌時才可用."
+L["Causes the entire screen to flash red momentarily"] = "令整個螢幕閃動紅色"
+L["Center"] = "中置"
+L["Center Omen"] = "中置Omen"
+L["|cffff0000Error:|r Omen cannot use shake warning if you have turned on nameplates at least once since logging in."] = "|cffff0000錯誤:|r 如果您在登錄後啟動過一次名牌,Omen 將無法使用震動警報功能."
+L["Clamp To Screen"] = "螢幕鎖定"
+L["Click|r to toggle the Omen window"] = "點擊|r開/關 Omen 視窗"
+-- L["Click Through"] = "Click Through"
+L["Collapse to show a minimum number of bars"] = "收起以顯示最小數量的棒條"
+L["Configure"] = "設定"
+L["Configure bar settings."] = "配置棒條設定."
+L["Configure title bar settings."] = "配置標題欄設定"
+L["Controls the frame strata of the main Omen window. Default: MEDIUM"] = "控制 Omen 主要視窗的框架階層。預設:MEDIUM(中)"
+L["Controls the scaling of the main Omen window."] = "控制 Omen 主視窗的尺寸."
+L["Controls the transparency of the main Omen window."] = "控制 Omen 主視窗的透明度."
+L["Controls whether the main Omen window can be dragged offscreen"] = "控制 Omen 主要視窗是否可以被拖動到螢幕外."
+L["Control the font size of the labels"] = "標籤字體大小"
+L["Control the font size of the title text"] = "標題文字字體大小"
+L["DEATHKNIGHT"] = "死亡騎士"
+L["Disable while tanking"] = "當為坦克時關閉警報"
+L["DISABLE_WHILE_TANKING_DESC"] = "如果在防禦姿態,熊形態,正義之怒與冰霜系時,不顯示警報."
+L["Display large numbers in Ks"] = "大數字時用千位(K)顯示"
+L["DRUID"] = "德魯伊"
+L["HERO"] = "Hero"
+L["Enable Screen Flash"] = "開啟螢幕閃動"
+L["Enable Screen Shake"] = "開啟螢幕震動"
+L["Enable Sound"] = "開啟音效"
+L["Enable Warning Message"] = "開啟警報消息"
+L["Fade/MI Bar Color"] = "漸隱/鏡像的顏色"
+L["FAQ Part 1"] = "FAQ 第1部分"
+L["FAQ Part 2"] = "FAQ 第2部分"
+L["Font"] = "字體"
+L["Font Color"] = "字體顏色"
+L["Font Outline"] = "字體輪廓"
+L["Font Size"] = "字體大小"
+L["Frame's background color"] = "框體背景顏色"
+L["Frame's border color"] = "框體邊框顏色"
+L["Frame Strata"] = "框架階層"
+L["Frequently Asked Questions"] = "常見問題"
+L["FuBar Options"] = "FuBar 選項"
+L["GENERAL_FAQ"] = [=[|cffffd200Omen3 與 Omen2 的區別|r
+
+Omen3 使用 Blizzard 所提供的仇恨應用程式界面,與 Omen2 不一樣,Omen3 不會去預測或者計算仇恨,仇恨值是通過應用程式界面直接向伺服器端獲取.
+
+Omen2 使用 Threat-2.0 數據庫,這個數據庫是根據檢測的戰鬥記錄,法術,增益與減益,姿態和裝備來計算不同的仇恨值.仇恨的計算是根據現有的資料與檢測所得到的數據計算的.很多技能是假設值,無法驗證(如擊退,我們假設擊退仇恨降低50%).
+
+Threat 2.0數據庫也包含了其他人都用這個數據庫的時候同步整個團隊的威脅值,這些威脅數據用來提供給整個團隊以供參考.
+
+從 Patch 3.0.2 開始 Omen 將不再做這些動作,Threat 資料庫不再需要.Omen3 所使用 Blizzard 內建威脅監視器,並從中獲取威脅資料,這也造成 Omen 不再需要同步資料,檢測戰鬥記錄與猜測數據.效果因減少資料的傳送而提升,包括 CPU 與內存的使用量都會減少,針對特殊首領的威脅變化事件也不再需要.
+
+更進一步說受益包括一些 NPC 對怪物的威脅,比如太陽之井高地人類形態的卡雷苟斯的威脅.但是也有一些不利的地方,威脅數據更新也變得慢了,團隊中如果沒有人的目標是那個 NPC 的話,他的威脅也無法得到,而你沒有直接參與的戰鬥你也無法獲得威脅(如你沒有造成任何傷害,或者製造任何威脅你將無法得到威脅值,就算你讓你的寵物上去攻擊而你沒有攻擊也一樣不能得到威脅值).
+
+|cffffd200我如何去除中間2條灰色垂直線?|r
+
+鎖定 Omen.鎖定 Omen 將防止它被移動或調整大小,以及防止欄被調整.如果你沒有注意到,那兩條灰色垂直線是用來調整欄的大小.
+
+|cffffd200如何將 Omen3 的外觀改成與 Omen2 類似?|r
+
+改變背景材質與邊框材質為 Blizzard Tooltip,將背景顏色改成黑色(通過拖動亮度條調節底部顏色),邊框顏色改為藍色.
+
+|cffffd200為什麼就算我再戰鬥中也看不到任何威脅值?|r
+
+除非你對怪物有作出任何傷害或者造成任何威脅,否則 Blizzard 仇恨應用程式界面不會給出任何威脅值,我們估計這可能是 Blizzard 為了減少網絡資料的傳送.
+
+|cffffd200有沒有辦法解決 Blizzard 的這個的限制?攻擊之前無法看到我的寵物的威脅.|r
+
+我們沒有辦法在短期內解決這個限制(正如 Omen2 沒有一樣).
+
+Omen3 的目標是提供準確的威脅數據,我們不再打算推測你的數據並降低你的 FPS.你需信任你的寵物/坦克,或者攻擊之前等待2秒並用低傷害法術(如冰槍)攻擊,這樣可以讓你可以得到初步的威脅讀數.
+]=]
+L["GENERAL_FAQ2"] = [=[|cffffd200我們能讓 AoE 模組回來嗎?|r
+
+同樣,如不使用猜測威脅值的方式是不可能做得到的. Blizzard 的仇恨應用程式界面僅限我們查詢團隊目標中某人的威脅數據.這意味著假如20個怪物中只有6個被團隊選定,那麼就沒有辦法獲得其他14個怪物的威脅數據.
+
+這也是極其複雜的猜測治療和增益效果(威脅值是跟據你在與怪物戰鬥的數量來攤分)因為怪物處於控制效果(變形術,放逐,悶棍)並沒有他們的仇恨列表並且插件不能準確的告知正在與多少個怪物戰鬥,Omen2 猜測的幾乎總是錯誤的.
+
+|cffffd200鼠標懸停單位顯示了威脅百分比與 Omen3 的仇恨百分比報告並不一致,為什麼?|r
+
+ Blizzard 的威脅比例調整為0%至100%,這樣你總是在100%是獲得仇恨.Omen 報告的沒有調整過,近站系獲得仇恨百分比為110%,遠程或其他的則為130%.
+
+普遍認為,怪物的主要目標被稱為坦克並被定義為獲得100%的威脅.
+
+|cffffd200Omen3 有同步資料或者分析戰鬥資料嘛?|r
+
+Omen3 不需要同步資料或者分析戰鬥資料,現階段沒有任何必要去這樣做.
+
+|cffffd200威脅資料更新太慢……|r
+
+Omen3 威脅值更新速度與 Blizzard 提供威脅數據給我們的速度是一樣的.
+
+事實上, Blizzard 每秒更新它們一次,這個速度大大超過了 Omen2 的同步頻率.在 Omen2,你只是每3秒(坦克每1.5秒)交換一次威脅數據.
+
+|cffffd200我要去哪裡回報 Bug 或者提出建議?|r
+
+http://forums.wowace.com/showthread.php?t=14249
+
+|cffffd200誰製作了 Omen3?|r
+
+Xinhuan (Blackrock US Alliance) 美服-黑石山-聯盟:Xinhuan.
+
+|cffffd200是否接受 Paypal(貝寶)捐助?|r
+
+是的,發送到xinhuan@gmail.com.
+]=]
+L["General Settings"] = "綜合配置"
+L["Grow bars upwards"] = "棒條向上增加"
+L["Heading background color"] = "主標題的背景顏色"
+L["Heading BG Color"] = "主標題的背景顏色"
+L["Height of each bar"] = "每個棒條的高度"
+L["Height of the title bar. The minimum height allowed is twice the background border thickness."] = "標題欄高度. 最小厚度為背景邊框厚度的一倍."
+L["Help File"] = "幫助文件"
+L["Hide minimap/FuBar icon"] = "隱藏小地圖/Fubar 小圖標"
+L["Hide Omen"] = "隱藏 Omen"
+L["Hide Omen entirely if it collapses to show 0 bars"] = "當沒有棒條時收起並隱藏 Omen"
+L["Hide Omen on 0 bars"] = "當沒有棒條時隱藏 Omen"
+L["However, hide Omen if any of the following are true (higher priority than the above)."] = "但是,選定以下項目時 Omen 將被隱藏(比以上項更具優先級)。"
+L["HUNTER"] = "獵人"
+L["Ignore Player Pets"] = "忽略玩家寵物"
+L["IGNORE_PLAYER_PETS_DESC"] = [=[讓 Omen 忽略敵對玩家寵物以確定顯示哪些單位的威脅.
+
+玩家寵物|cffffff78攻擊|r或者|cffffff78防禦|r狀態保持威脅與正常的怪物相同,正被攻擊目標具有最高的威脅.如果寵物指定攻擊一個具體目標,寵物仍然保持在威脅列表,但保持在指定的目標定義100%威脅之上.玩家寵物可以被嘲諷以攻擊你.
+
+然而,玩家寵物在|cffffff78被動|r模式並沒有威脅列表,嘲諷依然不起作用.它們只攻擊玩家所指定的目標且沒有仇恨列表.
+
+當玩家寵物處於|cffffff78跟隨|r狀態時,寵物的威脅列表被消除並立刻停止攻擊,雖然它可能會立即重新指定目標位於攻擊/防禦模式.
+]=]
+L["Invert Bar/Text Colors"] = "互換棒條/文本顏色"
+L["Left"] = "左邊"
+L["Lock Omen"] = "鎖定 Omen"
+L["Locks Omen in place and prevents it from being dragged or resized."] = "鎖定 Omen,使其無法移動或調整大小。"
+L["MAGE"] = "法師"
+-- L["Makes the Omen window non-interactive"] = "Makes the Omen window non-interactive"
+L["Max bars to show"] = "棒條的顯示數量"
+L["Max number of bars to show"] = "棒條最大顯示數量"
+L["'My Bar' BG Color"] = "「My Bar」背景顏色"
+L["Name"] = "名字"
+L["None"] = "無"
+L["*Not in Party*"] = "*不在隊伍中*"
+L["OMEN_DESC"] = "Omen 是一個佔用少量系統資源監控威脅的 UI,可以顯示你和同伴參與戰鬥中怪物的威脅列表.你可以改變 Omen 的外觀,並且根據不同的角色儲存不同的設定."
+L["Omen Quick Menu"] = "Omen 快捷選單"
+L["OMEN_SLASH_DESC"] = "這些按鈕與斜槓命令有相同的效果(/omen)"
+L["OMEN_WARNINGS_DESC"] = "這裡可以調整 Omen 在何時用何種方式給你提出仇恨警報."
+L["Open Config"] = "打開配置"
+L["Open Omen's configuration panel"] = "打開 Omen 配置面板"
+L["Open the configuration dialog"] = "打開 Omen 配置視窗"
+L["Outline"] = "輪廓"
+L["PALADIN"] = "聖騎士"
+L["Passed %s%% of %s's threat!"] = "已超過>%2$s<的%1$2.0f%%威脅!"
+L["PET"] = "寵物"
+L["Pet Bar Color"] = "寵物棒條顏色"
+L["Position"] = "位置"
+L["PRIEST"] = "牧師"
+L["Print a message to screen when you accumulate too much threat"] = "當你仇恨過高時在螢幕上顯示警報消息"
+L["Profiles"] = "配置檔"
+L["> Pull Aggro <"] = ">獲得仇恨<"
+L["Pull Aggro Bar Color"] = "獲得仇恨棒條顏色"
+L["Right"] = "右邊"
+L["Right-click|r to open the options menu"] = "右鍵|r打開選項選單"
+L["ROGUE"] = "盜賊"
+L["Scale"] = "縮放"
+L["Sets how far inside the frame the threat bars will display from the 4 borders of the frame"] = "威脅棒條顯示的內框與外框之間距離"
+L["SHAMAN"] = "薩滿"
+L["Short Numbers"] = "簡化數字"
+L["Show a bar for the amount of threat you will need to reach in order to pull aggro."] = "顯示獲得仇恨所需威脅數值的棒條."
+L["Show bars for these classes"] = "顯示的職業條"
+L["Show Classes..."] = "顯示職業"
+L["SHOW_CLASSES_DESC"] = "Omen 將顯示以下職業的威脅.除非你點選「不在隊伍中」的選項,否則 Omen 將只顯示你隊伍中的玩家."
+L["Show column headings"] = "顯示主標題欄"
+L["Show Headings"] = "顯示標題"
+L["Show icon"] = "顯示小圖標"
+L["Show minimap button"] = "顯示小地圖按鈕"
+L["Show Omen"] = "顯示 Omen"
+L["Show Omen when..."] = "當何時顯示 Omen"
+L["Show Omen when any of the following are true"] = "當符合以下條件時 Omen 會被顯示"
+L["Show Omen when you are alone"] = "當你沒有隊伍時顯示 Omen"
+L["Show Omen when you are in a 5-man party"] = "當你在5人隊伍中顯示 Omen"
+L["Show Omen when you are in a raid"] = "當你在團隊中顯示 Omen"
+L["Show Omen when you have a pet out"] = "當你的寵物存在時顯示 Omen"
+L["Show Pull Aggro Bar"] = "顯示獲得仇恨棒條"
+L["Show text"] = "顯示文字"
+L["Show the Omen minimap button"] = "顯示 Omen 小地圖按鈕"
+L["Show the Omen Title Bar"] = "顯示 Omen 標題欄"
+L["Show Threat %"] = "顯示威脅值%"
+L["Show threat per second values"] = "顯示每秒威脅值"
+L["Show Threat Values"] = "顯示威脅值"
+L["Show Title Bar"] = "顯示標題欄"
+L["Show TPS"] = "顯示 TPS"
+L["Show When..."] = "當何時顯示"
+L["Slash Command"] = "斜槓命令"
+L["Smoothly animate bar changes"] = "平滑動態棒條"
+L["Sound to play"] = "播放音效"
+L["Spacing between each bar"] = "棒條之間的距離"
+L["Switch the colors so that the bar background colors and the text colors are swapped."] = "將棒條背景顏色與文本顏色進行互換。"
+L["Tank Bar Color"] = "坦克棒條顏色"
+L["Tells Omen to additionally check your 'focus' and 'focustarget' before your 'target' and 'targettarget' in that order for threat display."] = "讓 Omen 額外檢查您的「focus」和「focus目標」位於「目標」和「目標的目標」之前的順序顯示威脅."
+L["Tells Omen to enter Test Mode so that you can configure Omen's display much more easily."] = "讓 Omen 進入測試模式,你可以更容易的配置 Omen 顯示."
+L["Test Mode"] = "測試模式"
+L["Test warnings"] = "測試警報"
+L["Texture to use for the frame's background"] = "框體背景材質"
+L["Texture to use for the frame's border"] = "框體邊框材質"
+L["The background color for all threat bars"] = "所有威脅棒條背景顏色"
+L["The background color for pets"] = "寵物棒條背景顏色"
+L["The background color for players under the effects of Fade and Mirror Image (they will be at negative 4 million threat)"] = "設定玩家在漸隱(牧師)和鏡像(法師)啟動中的背景顏色, (他們將設定在負4百萬威脅值)"
+L["The background color for your Pull Aggro bar"] = "獲得仇恨棒條背景顏色"
+L["The background color for your tank's threat bar"] = "坦克威脅棒條背景顏色"
+L["The background color for your threat bar"] = "威脅棒條的背景顏色"
+L["The color of the labels"] = "標籤顏色"
+L["The color of the title text"] = "標題字體顏色"
+L["The font that the labels will use"] = "標籤字體"
+L["The font that the title text will use"] = "標題文字字體"
+L["The outline that the labels will use"] = "標籤輪廓"
+L["The outline that the title text will use"] = "標題文字字體輪廓"
+L["The size used to tile the background texture"] = "標題背景材質尺寸"
+L["The texture that the bar will use"] = "棒條材質"
+L["The thickness of the border"] = "邊框厚度"
+L["Thick Outline"] = "加倍輪廓"
+L["This section controls when Omen is automatically shown or hidden."] = "這些設定將控制 Omen 何時自動顯示或隱藏."
+L["Threat"] = "威脅"
+L["Threat [%]"] = "威脅[%]"
+L["Tile Background"] = "標題背景"
+L["Tile the background texture"] = "標題背景材質"
+L["Title Bar Background Options"] = "標題欄背景選項"
+L["Title Bar Height"] = "標題欄高度"
+L["Title Bar Settings"] = "標題欄設定"
+L["Title Text Options"] = "標題文字選項"
+L["Toggle Focus"] = "開/關focus"
+L["Toggle Omen"] = "開/關 Omen"
+L["TPS"] = "TPS"
+L["TPS Window"] = "TPS 視窗"
+L["TPS_WINDOW_DESC"] = "每秒威脅值的計算是根據最後X秒視窗內的變化而決定的."
+L["Turning this on will cause Omen to hide whenever you are in a battleground or arena."] = "啟用此項後當您位於戰場或競技場時將會使 Omen 隱藏."
+L["Turning this on will cause Omen to hide whenever you are in a city or inn."] = "啟用此項時當您位於城市或者旅店時將會使 Omen 隱藏."
+L["Turning this on will cause Omen to hide whenever you are not in combat."] = "啟用此項後當您離開戰鬥後 Omen 將會被隱藏."
+L[""] = "<未知的>"
+L["Use a different colored background for the tank's threat bar in Omen"] = "為 Omen 坦克威脅棒條使用不同背景顏色."
+L["Use a different colored background for your threat bar in Omen"] = "在 Omen 中為威脅棒條使用不同的背景顏色"
+L["Use Auto Show/Hide"] = "使用自動顯示/隱藏"
+L["Use !ClassColors"] = "使用 !ClassColors"
+L["Use Class Colors"] = "使用職業顏色"
+L["Use !ClassColors addon for class colors for the background color of threat bars"] = "使用 !ClassColors 職業顏色為威脅條背景顏色。"
+L["Use Focus Target"] = "使用focus目標"
+L["Use 'My Bar' color"] = "使用「My Bar」顏色"
+L["Use Same Background"] = "使用同樣背景"
+L["Use standard class colors for the background color of threat bars"] = "使用標準職業顏色威脅棒條背景顏色"
+L["Use Tank Bar color"] = "使用坦克棒條顏色"
+L["Use the same background settings for the title bar as the main window's background"] = "標題欄使用如同與主視窗同樣的背景設定"
+L["WARLOCK"] = "術士"
+L["Warning Settings"] = "警報設定"
+L["Warning Threshold %"] = "警告臨界值(百分比)"
+L["Warrior"] = "戰士"
+L["WARRIOR"] = "戰士"
+L["WARRIOR_FAQ"] = [=[以下數據來自|cffffd200http://www.tankspot.com/forums/f200/39775-wow-3-0-threat-values.html|r2008年10月2號(Satrina 的功勞).等級80採集的數據.
+
+|cffffd200狀態|r
+戰鬥姿態 ________ x 80
+狂暴姿態 _____ x 80
+戰術掌握 _____ x 121/142/163
+防禦姿態 _____ x 207.35
+
+請注意,在我們原來估計的威脅(使用魔獸世界2.0),我們把1傷害等同1威脅,並因姿態和挑釁乘以1.495.我們看到 Blizzard 的做法未乘以小數點後面的數字,所以在2.x中,將已經x149(或者x149.5);這是x207(或者207.3)在3.0版本.我想這是允許的傳輸整數值不是小數值在網際網路上的效率.看來,威脅值乘以207.35在伺服器上,然後四捨五入.
+
+如果您仍希望使用1傷害=1威脅的方法,姿態為是0.8和2.0735,等等.
+
+|cffffd200威脅值(姿態適用,除非另有說明):|r
+戰鬥怒吼 _________ 78 (split)
+順劈斬 _______________ damage + 225 (split)
+命令怒吼 _____ 80 (split)
+震盪猛擊 ______ damage only
+傷害反射護盾 ________ damage only
+挫志怒吼 ___ 63 (split)
+毀滅打擊 ____________ damage + 5% of AP *** Needs re-checking for 8982 **
+躲閃/招架/格擋_____ 1 (in defensive stance with Improved Defensive Stance only)
+英勇打擊 ________ damage + 259
+英勇投擲 _________ 1.50 x damage
+怒氣獲得 ____________ 5 (stance modifier is not applied)
+撕裂 _________________ damage only
+復仇 ______________ damage + 121
+盾擊 __________ 36
+盾牌猛擊 __________ damage + 770
+震盪波 ____________ damage only
+猛擊 _________________ damage + 140
+法術反射 ________ damage only (only for spells aimed at you)
+社會仇恨 _________ 0
+破甲攻擊 ________ 345 + 5%AP
+雷霆一擊 _________ 1.85 x damage
+警戒 ____________ 10% of target's generated threat (stance modifier is not applied)
+
+如果你通過加強反彈魔法天賦幫助隊友反彈魔法,你不會獲得任何仇恨值,相對如果你通過此天賦幫助隊友反彈魔法,這個仇恨值將計算在隊友身上.
+]=]
+L["You are alone"] = "無隊伍"
+L["You are in a battleground"] = "當你在戰場"
+L["You are in a party"] = "有隊伍"
+L["You are in a raid"] = "有團隊"
+L["You are not in combat"] = "你不在戰鬥"
+L["You are resting"] = "當你在休息"
+L["You have a pet"] = "有寵物"
+
diff --git a/Omen.lua b/Omen.lua
index 3966b60..1f6a2fa 100644
--- a/Omen.lua
+++ b/Omen.lua
@@ -1,3147 +1,3157 @@
------------------------------------------------------------------------------
--- Addon declaration
-local Omen = LibStub("AceAddon-3.0"):NewAddon("Omen", "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0", "LibSink-2.0")
-local L = LibStub("AceLocale-3.0"):GetLocale("Omen", false)
-local LSM = LibStub("LibSharedMedia-3.0")
-local LDB = LibStub("LibDataBroker-1.1", true)
-local LDBIcon = LDB and LibStub("LibDBIcon-1.0", true)
-Omen.version = GetAddOnMetadata("Omen", "Version")
-Omen.versionstring = "Omen v"..GetAddOnMetadata("Omen", "Version")
-_G["Omen"] = Omen
-
-
------------------------------------------------------------------------------
--- Keybinding globals
-BINDING_HEADER_OMEN = Omen.versionstring
-BINDING_NAME_OMENTOGGLE = L["Toggle Omen"]
-BINDING_NAME_OMENTOGGLEFOCUS = L["Toggle Focus"]
-
-
------------------------------------------------------------------------------
--- Register some media
-LSM:Register("sound", "Rubber Ducky", [[Sound\Doodad\Goblin_Lottery_Open01.wav]])
-LSM:Register("sound", "Cartoon FX", [[Sound\Doodad\Goblin_Lottery_Open03.wav]])
-LSM:Register("sound", "Explosion", [[Sound\Doodad\Hellfire_Raid_FX_Explosion05.wav]])
-LSM:Register("sound", "Shing!", [[Sound\Doodad\PortcullisActive_Closed.wav]])
-LSM:Register("sound", "Wham!", [[Sound\Doodad\PVP_Lordaeron_Door_Open.wav]])
-LSM:Register("sound", "Simon Chime", [[Sound\Doodad\SimonGame_LargeBlueTree.wav]])
-LSM:Register("sound", "War Drums", [[Sound\Event Sounds\Event_wardrum_ogre.wav]])
-LSM:Register("sound", "Cheer", [[Sound\Event Sounds\OgreEventCheerUnique.wav]])
-LSM:Register("sound", "Humm", [[Sound\Spells\SimonGame_Visual_GameStart.wav]])
-LSM:Register("sound", "Short Circuit", [[Sound\Spells\SimonGame_Visual_BadPress.wav]])
-LSM:Register("sound", "Fel Portal", [[Sound\Spells\Sunwell_Fel_PortalStand.wav]])
-LSM:Register("sound", "Fel Nova", [[Sound\Spells\SeepingGaseous_Fel_Nova.wav]])
-LSM:Register("sound", "You Will Die!", [[Sound\Creature\CThun\CThunYouWillDIe.wav]])
-LSM:Register("sound", "Omen: Aoogah!", [[Interface\AddOns\Omen\aoogah.ogg]])
-
-
------------------------------------------------------------------------------
--- Localize some global functions
-local floor, format, random, pairs, type = floor, format, random, pairs, type
-local tinsert, tremove, next, sort, wipe = tinsert, tremove, next, sort, wipe
-local RAID_CLASS_COLORS = RAID_CLASS_COLORS
-local UnitDetailedThreatSituation = UnitDetailedThreatSituation
-local UnitExists, UnitGUID, UnitName, UnitClass, UnitHealth = UnitExists, UnitGUID, UnitName, UnitClass, UnitHealth
-local UnitIsPlayer, UnitPlayerControlled, UnitCanAttack = UnitIsPlayer, UnitPlayerControlled, UnitCanAttack
-local GetNumRaidMembers, GetNumPartyMembers = GetNumRaidMembers, GetNumPartyMembers
-
-
------------------------------------------------------------------------------
--- Local variables used
-local db
-local defaults = {
- profile = {
- Alpha = 1,
- Scale = 1,
- GrowUp = false,
- Autocollapse = false,
- NumBars = 10,
- CollapseHide = false,
- Locked = false,
- PositionW = 200,
- PositionH = 82,
- VGrip1 = 85,
- VGrip2 = 115,
- UseFocus = false,
- IgnorePlayerPets = true,
- FrameStrata = "3-MEDIUM",
- ClampToScreen = true,
- ClickThrough = false,
- Background = {
- Texture = "Blizzard Parchment",
- BorderTexture = "Blizzard Dialog",
- Color = {r = 1, g = 1, b = 1, a = 1,},
- BorderColor = {r = 0.8, g = 0.6, b = 0, a = 1,},
- Tile = false,
- TileSize = 32,
- EdgeSize = 8,
- BarInset = 3,
- },
- TitleBar = {
- Height = 16,
- Font = "Friz Quadrata TT",
- FontOutline = "",
- FontColor = {r = 1, g = 1, b = 1, a = 1,},
- FontSize = 10,
- ShowTitleBar = true,
- UseSameBG = true,
- Texture = "Blizzard Parchment",
- BorderTexture = "Blizzard Dialog",
- Color = {r = 1, g = 1, b = 1, a = 1,},
- BorderColor = {r = 0.8, g = 0.6, b = 0, a = 1,},
- Tile = false,
- TileSize = 32,
- EdgeSize = 8,
- },
- Bar = {
- Texture = "Blizzard",
- Height = 12,
- Spacing = 0,
- AnimateBars = true,
- ShortNumbers = true,
- Font = "Friz Quadrata TT",
- FontOutline = "",
- FontColor = {r = 1, g = 1, b = 1, a = 1,},
- FontSize = 10,
- Classes = {
- DEATHKNIGHT = true,
- DRUID = true,
- HUNTER = true,
- MAGE = true,
- PALADIN = true,
- PET = true,
- PRIEST = true,
- ROGUE = true,
- SHAMAN = true,
- WARLOCK = true,
- WARRIOR = true,
- ["*NOTINPARTY*"] = true,
- },
- ShowTPS = true,
- TPSWindow = 10,
- ShowHeadings = true,
- HeadingBGColor = {r = 0, g = 0, b = 0, a = 0,},
- UseMyBarColor = false,
- MyBarColor = {r = 1, g = 0, b = 0, a = 1,},
- ShowPercent = true,
- ShowValue = true,
- UseClassColors = true,
- BarColor = {r = 1, g = 0, b = 0, a = 1,},
- UseTankBarColor = false,
- TankBarColor = {r = 1, g = 0, b = 0, a = 1,},
- AlwaysShowSelf = true,
- ShowAggroBar = true,
- AggroBarColor = {r = 1, g = 0, b = 0, a = 1,},
- PetBarColor = {r = 0.77, g = 0, b = 1, a = 1},
- FadeBarColor = {r = 0.5, g = 0.5, b = 0.5, a = 1},
- UseCustomClassColors = true,
- InvertColors = false,
- },
- ShowWith = {
- UseShowWith = true,
- Pet = true,
- Alone = false,
- Party = true,
- Raid = true,
- -- Deprecated SV values
- -- Resting = false, PVP = false, Dungeon = true, ShowOnlyInCombat = false,
- HideWhileResting = true,
- HideInPVP = true,
- HideWhenOOC = false,
- },
- FuBar = {
- HideMinimapButton = true,
- AttachMinimap = false,
- },
- Warnings = {
- Sound = true,
- Flash = true,
- Shake = false,
- Message = false,
- SinkOptions = {},
- Threshold = 90,
- SoundFile = "Fel Nova",
- DisableWhileTanking = true,
- },
- MinimapIcon = {
- hide = false,
- minimapPos = 220,
- radius = 80,
- },
- },
-}
-local guidNameLookup = {} -- Format: guidNameLookup[guid] = "Unit Name"
-local guidClassLookup = {} -- Format: guidClassLookup[guid] = "CLASS"
-local timers = {} -- Format: timers.timerName = timer returned from AceTimer-3.0
-local bars = {} -- Format: bars[i] = frame containing the i-th bar from the top of Omen
-local inRaid, inParty -- boolean variables indicating if the player is in a raid and/or party
-local testMode = false -- boolean: Are we in test mode?
-local manualToggle = false -- boolean: Did we manually toggle Omen?
-local moduleOptions = {} -- Table for LoD module options registration
-
-Omen.GuidNameLookup = guidNameLookup
-Omen.GuidClassLookup = guidClassLookup
-Omen.Timers = timers
-Omen.Bars = bars
-setmetatable(guidNameLookup, {__index = function(self, guid) return L[""] end})
-
--- For speedups. Rather than concantenating every time we need a unitID, we just look
--- it up instead. That is rID[i] is much faster than format("raid%d", i) or "raid"..i
-local pID = {}
-local ptID = {}
-local ppID = {}
-local pptID = {}
-local rID = {}
-local rtID = {}
-local rpID = {}
-local rptID = {}
-for i = 1, 4 do
- pID[i] = format("party%d", i)
- ptID[i] = format("party%dtarget", i)
- ppID[i] = format("partypet%d", i)
- pptID[i] = format("partypet%dtarget", i)
-end
-for i = 1, 40 do
- rID[i] = format("raid%d", i)
- rtID[i] = format("raid%dtarget", i)
- rpID[i] = format("raidpet%d", i)
- rptID[i] = format("raidpet%dtarget", i)
-end
-local showClassesOptionTable = {
- DEATHKNIGHT = L["DEATHKNIGHT"],
- DRUID = L["DRUID"],
- HUNTER = L["HUNTER"],
- MAGE = L["MAGE"],
- PALADIN = L["PALADIN"],
- PET = L["PET"],
- PRIEST = L["PRIEST"],
- ROGUE = L["ROGUE"],
- SHAMAN = L["SHAMAN"],
- WARLOCK = L["WARLOCK"],
- WARRIOR = L["WARRIOR"],
- ["*NOTINPARTY*"] = L["*Not in Party*"],
-}
-
-
-----------------------------------------------------------------------------------------
--- Use a common frame and setup some common functions for the Omen dropdown menus
-local Omen_DropDownMenu = CreateFrame("Frame", "Omen_DropDownMenu")
-Omen_DropDownMenu.displayMode = "MENU"
-Omen_DropDownMenu.info = {}
-Omen_DropDownMenu.HideMenu = function()
- if UIDROPDOWNMENU_OPEN_MENU == Omen_DropDownMenu then
- CloseDropDownMenus()
- end
-end
-Omen_DropDownMenu.OnClick = function(frame, button, down)
- if Omen_DropDownMenu.initialize ~= frame.initMenuFunc then
- CloseDropDownMenus()
- Omen_DropDownMenu.initialize = frame.initMenuFunc
- end
- ToggleDropDownMenu(1, nil, Omen_DropDownMenu, frame, 0, 0)
-end
-
-
------------------------------------------------------------------------------
--- Table Pool for recycling tables
-local tablePool = {}
-setmetatable(tablePool, {__mode = "kv"}) -- Weak table
-
--- Get a new table
-local function newTable()
- local t = next(tablePool) or {}
- tablePool[t] = nil
- return t
-end
-
--- Delete table and return to pool -- Recursive!! -- Use with care!!
-local function delTable(t)
- if type(t) == "table" then
- for k, v in pairs(t) do
- if type(v) == "table" then
- delTable(v) -- child tables get put into the pool
- end
- t[k] = nil
- end
- t[true] = true -- resize table to 1 item
- t[true] = nil
- setmetatable(t, nil)
- tablePool[t] = true
- end
- return nil -- return nil to assign input reference
-end
-
-
------------------------------------------------------------------------------
--- Omen initialization and frame functions
-
-local function startmoving(self)
- if not db.Locked then
- Omen.Anchor.IsMovingOrSizing = 1
- Omen.Anchor:StartMoving()
- end
-end
-local function stopmoving(self)
- if Omen.Anchor.IsMovingOrSizing then
- Omen.Anchor:StopMovingOrSizing()
- Omen:SetAnchors()
- Omen:UpdateBars()
- Omen.Anchor.IsMovingOrSizing = nil
- end
- if self == Omen.Anchor then db.Shown = self:IsShown() end
-end
-local function sizing(self)
- local w = Omen.Anchor:GetWidth()
- db.VGrip1 = w * Omen.Anchor.VGrip1Ratio
- db.VGrip2 = w * Omen.Anchor.VGrip2Ratio
- if db.VGrip1 < 10 then db.VGrip1 = 10 end
- if db.VGrip1 > w - 10 then db.VGrip1 = w - 10 end
- if db.Bar.ShowTPS then
- if db.VGrip2 < db.VGrip1 + 10 then db.VGrip2 = db.VGrip1 + 10 end
- if db.VGrip1 > w - 20 then
- db.VGrip1 = w - 20
- db.VGrip2 = w - 10
- end
- Omen.VGrip2:ClearAllPoints()
- Omen.VGrip2:SetPoint("TOPLEFT", Omen.BarList, "TOPLEFT", db.VGrip2, 0)
- Omen.VGrip2:SetPoint("BOTTOMLEFT", Omen.BarList, "BOTTOMLEFT", db.VGrip2, 0)
- end
- Omen.VGrip1:ClearAllPoints()
- Omen.VGrip1:SetPoint("TOPLEFT", Omen.BarList, "TOPLEFT", db.VGrip1, 0)
- Omen.VGrip1:SetPoint("BOTTOMLEFT", Omen.BarList, "BOTTOMLEFT", db.VGrip1, 0)
- Omen:ResizeBars()
- Omen:ReAnchorLabels()
- Omen:UpdateBars()
-end
-local function movegrip1(self)
- local x = GetCursorPosition() / UIParent:GetEffectiveScale() / Omen.Anchor:GetScale()
- local x1 = Omen.Anchor:GetLeft() + 10
- local x2 = db.Bar.ShowTPS and Omen.Anchor:GetLeft() + db.VGrip2 - 10 or Omen.Anchor:GetRight() - 10
- if x > x1 and x < x2 then
- db.VGrip1 = x - x1 + 10
- Omen.VGrip1:ClearAllPoints()
- Omen.VGrip1:SetPoint("TOPLEFT", Omen.BarList, "TOPLEFT", db.VGrip1, 0)
- Omen.VGrip1:SetPoint("BOTTOMLEFT", Omen.BarList, "BOTTOMLEFT", db.VGrip1, 0)
- end
- Omen:ReAnchorLabels()
-end
-local function movegrip2(self)
- local x = GetCursorPosition() / UIParent:GetEffectiveScale() / Omen.Anchor:GetScale()
- local x1 = Omen.Anchor:GetLeft() + db.VGrip1 + 10
- local x2 = Omen.Anchor:GetRight() - 10
- if x > x1 and x < x2 then
- db.VGrip2 = x - x1 + db.VGrip1 + 10
- Omen.VGrip2:ClearAllPoints()
- Omen.VGrip2:SetPoint("TOPLEFT", Omen.BarList, "TOPLEFT", db.VGrip2, 0)
- Omen.VGrip2:SetPoint("BOTTOMLEFT", Omen.BarList, "BOTTOMLEFT", db.VGrip2, 0)
- end
- Omen:ReAnchorLabels()
-end
-
-function Omen:CreateFrames()
- -- Create anchor
- self.Anchor = CreateFrame("Frame", "OmenAnchor", UIParent)
- self.Anchor:SetResizable(true)
- self.Anchor:SetMinResize(90, db.Background.EdgeSize * 2)
- self.Anchor:SetMovable(true)
- self.Anchor:SetPoint("CENTER", UIParent, "CENTER")
- self.Anchor:SetWidth(225)
- self.Anchor:SetHeight(150)
- self.Anchor:SetScript("OnHide", stopmoving)
- self.Anchor:SetScript("OnShow", function(self) db.Shown = true Omen:UpdateBars() end)
-
- -- Create Title
- self.Title = CreateFrame("Button", "OmenTitle", self.Anchor)
- self.Title:SetPoint("TOPLEFT", self.Anchor, "TOPLEFT")
- self.Title:SetPoint("TOPRIGHT", self.Anchor, "TOPRIGHT")
- self.Title:SetHeight(16)
- self.Title:SetMinResize(90, db.Background.EdgeSize * 2)
- self.Title:EnableMouse(true)
- self.Title:SetScript("OnMouseDown", startmoving)
- self.Title:SetScript("OnMouseUp", stopmoving)
- self.Title:SetScript("OnClick", Omen_DropDownMenu.OnClick)
- self.Title.initMenuFunc = self.TitleQuickMenu
- self.Title:RegisterForClicks("RightButtonUp")
-
- -- Create Title text
- self.TitleText = self.Title:CreateFontString(nil, nil, "GameFontNormal")
- self.TitleText:SetPoint("LEFT", self.Title, "LEFT", 8, 1)
- self.TitleText:SetJustifyH("LEFT")
- self.TitleText:SetTextColor(1, 1, 1, 1)
- self.defaultTitle = "Omen|cffffcc003|r"
- self.TitleText:SetText(self.defaultTitle)
-
- -- Create Bar List
- self.BarList = CreateFrame("Frame", "OmenBarList", self.Anchor)
- self.BarList:SetResizable(true)
- self.BarList:EnableMouse(true)
- self.BarList:SetPoint("TOPLEFT", self.Title, "BOTTOMLEFT")
- self.BarList:SetPoint("BOTTOMRIGHT", self.Anchor, "BOTTOMRIGHT")
- self.BarList:SetScript("OnMouseDown", startmoving)
- self.BarList:SetScript("OnMouseUp", stopmoving)
- self.BarList:SetScript("OnHide", stopmoving)
- self.BarList.barsShown = 0
-
- -- Create resizing corner grip
- self.Grip = CreateFrame("Button", "OmenResizeGrip", self.BarList)
- self.Grip:SetNormalTexture("Interface\\AddOns\\Omen\\ResizeGrip")
- self.Grip:SetHighlightTexture("Interface\\AddOns\\Omen\\ResizeGrip")
- self.Grip:SetWidth(16)
- self.Grip:SetHeight(16)
- self.Grip:SetPoint("BOTTOMRIGHT", self.BarList, "BOTTOMRIGHT", 0, 1)
- self.Grip:SetScript("OnMouseDown", function(self, button)
- if not db.Locked then
- Omen.Anchor.IsMovingOrSizing = 2
- Omen.Anchor.VGrip1Ratio = db.VGrip1 / Omen.Anchor:GetWidth()
- Omen.Anchor.VGrip2Ratio = db.VGrip2 / Omen.Anchor:GetWidth()
- Omen.Anchor:SetScript("OnSizeChanged", sizing)
- Omen.Anchor:StartSizing()
- end
- end)
- self.Grip:SetScript("OnMouseUp", function(self)
- if Omen.Anchor.IsMovingOrSizing == 2 then
- Omen.Anchor:SetScript("OnSizeChanged", nil)
- Omen.Anchor:StopMovingOrSizing()
- sizing()
- Omen:SetAnchors()
- Omen.Anchor.IsMovingOrSizing = nil
- Omen.Anchor.VGrip1Ratio = nil
- end
- end)
- self.Grip:SetScript("OnHide", self.Grip:GetScript("OnMouseUp"))
-
- -- Create label resizing vertical grip 1
- self.VGrip1 = CreateFrame("Button", "OmenVResizeGrip1", self.BarList)
- self.VGrip1:SetWidth(1)
- self.VGrip1:SetPoint("TOPLEFT", self.BarList, "TOPLEFT", db.VGrip1, 0)
- self.VGrip1:SetPoint("BOTTOMLEFT", self.BarList, "BOTTOMLEFT", db.VGrip1, 0)
- self.VGrip1:SetNormalTexture("Interface\\Tooltips\\UI-Tooltip-Background")
- self.VGrip1:SetHighlightTexture("Interface\\Tooltips\\UI-Tooltip-Background")
- self.VGrip1:GetNormalTexture():SetVertexColor(1, 1, 1, 0.5)
- self.VGrip1:GetHighlightTexture():SetVertexColor(1, 1, 1, 0.5)
- self.VGrip1:SetScript("OnMouseDown", function(self)
- if not db.Locked then self:SetScript("OnUpdate", movegrip1) end
- end)
- self.VGrip1:SetScript("OnMouseUp", function(self) self:SetScript("OnUpdate", nil) end)
- self.VGrip1:SetScript("OnHide", self.VGrip1:GetScript("OnMouseUp"))
- self.VGrip1:SetFrameLevel(self.BarList:GetFrameLevel() + 2)
-
- -- Create label resizing vertical grip 2
- self.VGrip2 = CreateFrame("Button", "OmenVResizeGrip2", self.BarList)
- self.VGrip2:SetWidth(1)
- self.VGrip2:SetPoint("TOPLEFT", self.BarList, "TOPLEFT", db.VGrip2, 0)
- self.VGrip2:SetPoint("BOTTOMLEFT", self.BarList, "BOTTOMLEFT", db.VGrip2, 0)
- self.VGrip2:SetNormalTexture("Interface\\Tooltips\\UI-Tooltip-Background")
- self.VGrip2:SetHighlightTexture("Interface\\Tooltips\\UI-Tooltip-Background")
- self.VGrip2:GetNormalTexture():SetVertexColor(1, 1, 1, 0.5)
- self.VGrip2:GetHighlightTexture():SetVertexColor(1, 1, 1, 0.5)
- self.VGrip2:SetScript("OnMouseDown", function(self)
- if not db.Locked then self:SetScript("OnUpdate", movegrip2) end
- end)
- self.VGrip2:SetScript("OnMouseUp", self.VGrip1:GetScript("OnMouseUp"))
- self.VGrip2:SetScript("OnHide", self.VGrip1:GetScript("OnMouseUp"))
- self.VGrip2:SetFrameLevel(self.BarList:GetFrameLevel() + 2)
-
- --[[self.FocusButton = CreateFrame("Button", "OmenFocusButton", self.Title, "OptionsButtonTemplate")
- self.FocusButton:SetWidth(16)
- self.FocusButton:SetHeight(16)
- self.FocusButton:SetPoint("TOPRIGHT")
- self.FocusButton:SetText("F")
- self.FocusButton:SetScript("OnClick", function(self, button, down)
- db.UseFocus = not db.UseFocus
- if db.UseFocus then
- self:GetFontString():SetTextColor(1, 0.82, 0, 1)
- else
- self:GetFontString():SetTextColor(0.5, 0.5, 0.5, 1)
- end
- Omen:UpdateBars()
- end)
- if db.UseFocus then
- self.FocusButton:GetFontString():SetTextColor(1, 0.82, 0, 1)
- else
- self.FocusButton:GetFontString():SetTextColor(0.5, 0.5, 0.5, 1)
- end]]
-
- self.CreateFrames = nil
-end
-
-function Omen:OnInitialize()
- -- Create savedvariables
- self.db = LibStub("AceDB-3.0"):New("Omen3DB", defaults)
- self.db.RegisterCallback(self, "OnProfileChanged", "OnProfileChanged")
- self.db.RegisterCallback(self, "OnProfileCopied", "OnProfileChanged")
- self.db.RegisterCallback(self, "OnProfileReset", "OnProfileChanged")
- db = self.db.profile
- self:SetSinkStorage(db.Warnings.SinkOptions)
-
- LSM.RegisterCallback(self, "LibSharedMedia_Registered", "UpdateUsedMedia")
-
- -- These 2 functions self GC after running
- self:CreateFrames()
- self:SetupOptions()
-
- self:RegisterEvent("PLAYER_LOGIN")
- self.OnInitialize = nil
-end
-
-function Omen:PLAYER_LOGIN()
- -- We set up anchors here because we only want to do it once on
- -- PLAYER_LOGIN, hence we don't do it in OnEnable() which triggers on
- -- the same event as well as on every subsequent Enable()/Disable() calls.
- -- It cannot be earlier than PLAYER_LOGIN because layout-cache.txt
- -- is loaded just before this event fires.
- self:SetAnchors(true)
- self.Anchor:SetAlpha(db.Alpha)
- self.Anchor:SetFrameStrata(strsub(db.FrameStrata, 3))
- self.Anchor:SetClampedToScreen(db.ClampToScreen)
- self:UpdateBackdrop()
- self:UpdateTitleBar()
- self:UpdateGrips()
- self:UpdateClickThrough()
- self:UpdateRaidClassColors()
- self:ClearAll()
- self:UnregisterEvent("PLAYER_LOGIN")
- if not db.Shown then self.Anchor:Hide() end -- Auto-show/hide will override this later if enabled
-
- -- Optional !ClassColors addon support
- if CUSTOM_CLASS_COLORS then
- CUSTOM_CLASS_COLORS:RegisterCallback("UpdateBars", self)
- end
-
- -- ConfigMode support
- do
- CONFIGMODE_CALLBACKS = CONFIGMODE_CALLBACKS or {}
- local oldTestMode = testMode
- local oldLocked = db.Locked
- function CONFIGMODE_CALLBACKS.Omen(action)
- if action == "ON" then
- oldTestMode = testMode
- oldLocked = db.Locked
- testMode = true
- db.Locked = false
- Omen:Toggle(true)
- elseif action == "OFF" then
- testMode = oldTestMode
- db.Locked = oldLocked
- manualToggle = false
- Omen:UpdateVisible()
- end
- Omen:UpdateGrips()
- Omen:UpdateBars()
- LibStub("AceConfigRegistry-3.0"):NotifyChange("Omen")
- end
- end
-
- -- LDB launcher
- if LDB then
- OmenLauncher = LDB:NewDataObject("Omen", {
- type = "launcher",
- icon = "Interface\\AddOns\\Omen\\icon",
- OnClick = function(clickedframe, button)
- if button == "RightButton" then Omen:ShowConfig() else Omen:Toggle() end
- end,
- OnTooltipShow = function(tt)
- tt:AddLine(self.defaultTitle)
- tt:AddLine("|cffffff00" .. L["Click|r to toggle the Omen window"])
- tt:AddLine("|cffffff00" .. L["Right-click|r to open the options menu"])
- end,
- })
- if LDBIcon and not IsAddOnLoaded("Broker2FuBar") and not IsAddOnLoaded("FuBar") then
- LDBIcon:Register("Omen", OmenLauncher, db.MinimapIcon)
- end
- end
-
- -- Optional launcher support for LFBP-3.0 if present, this code is placed here so
- -- that it runs after all other addons have loaded since we don't embed LFBP-3.0
- -- Yes, this is one big hack since LFBP-3.0 is a Rock library, and we embed it
- -- via Ace3. OnEmbedInitialize() needs to be called manually.
- if LibStub:GetLibrary("LibFuBarPlugin-3.0", true) and not IsAddOnLoaded("FuBar2Broker") then
- local LFBP = LibStub:GetLibrary("LibFuBarPlugin-3.0")
- LibStub("AceAddon-3.0"):EmbedLibrary(self, "LibFuBarPlugin-3.0")
- self:SetFuBarOption("tooltipType", "GameTooltip")
- self:SetFuBarOption("hasNoColor", true)
- self:SetFuBarOption("cannotDetachTooltip", true)
- self:SetFuBarOption("hideWithoutStandby", true)
- self:SetFuBarOption("iconPath", [[Interface\AddOns\Omen\icon]])
- self:SetFuBarOption("hasIcon", true)
- self:SetFuBarOption("defaultPosition", "RIGHT")
- self:SetFuBarOption("tooltipHiddenWhenEmpty", true)
- self:SetFuBarOption("configType", "None")
- LFBP:OnEmbedInitialize(self)
- function Omen:OnUpdateFuBarTooltip()
- GameTooltip:AddLine(self.defaultTitle)
- GameTooltip:AddLine("|cffffff00" .. L["Click|r to toggle the Omen window"])
- GameTooltip:AddLine("|cffffff00" .. L["Right-click|r to open the options menu"])
- end
- function Omen:OnFuBarClick(button)
- if button == "RightButton" then self:ShowConfig() else self:Toggle() end
- end
- self.optionsFrames["FuBar"] = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("Omen", L["FuBar Options"], self.versionstring, "FuBar")
- self:UpdateFuBarSettings()
- end
-
- self.PLAYER_LOGIN = nil
-end
-
-function Omen:OnEnable()
- self:RegisterEvent("UNIT_THREAT_LIST_UPDATE")
- self:RegisterEvent("UNIT_THREAT_SITUATION_UPDATE")
- self:RegisterEvent("PLAYER_TARGET_CHANGED")
-
- self:RegisterEvent("PARTY_MEMBERS_CHANGED")
- self:RegisterEvent("UNIT_PET", "PARTY_MEMBERS_CHANGED")
- self:RegisterEvent("UNIT_NAME_UPDATE", "PARTY_MEMBERS_CHANGED")
- self:RegisterEvent("PLAYER_PET_CHANGED", "PARTY_MEMBERS_CHANGED")
- --self:RegisterEvent("RAID_ROSTER_UPDATE", "PARTY_MEMBERS_CHANGED") -- Is this needed?
-
- self:RegisterEvent("PLAYER_UPDATE_RESTING", "UpdateVisible")
- self:RegisterEvent("PLAYER_ENTERING_WORLD")
-
- if db.ShowWith.HideWhenOOC then
- self:RegisterEvent("PLAYER_REGEN_DISABLED", "UpdateVisible")
- self:RegisterEvent("PLAYER_REGEN_ENABLED", "UpdateVisible")
- end
- if db.UseFocus then
- self:RegisterEvent("UNIT_TARGET")
- end
-
- self:PARTY_MEMBERS_CHANGED()
- self:PLAYER_TARGET_CHANGED()
-end
-
-function Omen:OnDisable()
- -- Cancel all timers (well at least nil them all
- -- out in timers[], since AceTimer-3.0 cancels
- -- them all OnDisable anyway).
- for k, v in pairs(timers) do
- self:CancelTimer(v, true)
- timers[k] = nil
- end
-
- self:_toggle(false)
-end
-
-function Omen:OnProfileChanged(event, database, newProfileKey)
- db = database.profile
- self:SetAnchors(true)
- self.Anchor:SetAlpha(db.Alpha)
- self.Anchor:SetFrameStrata(strsub(db.FrameStrata, 3))
- self.Anchor:SetClampedToScreen(db.ClampToScreen)
- self:UpdateBackdrop()
- self:UpdateTitleBar()
- self:UpdateGrips()
- self:ResizeBars()
- self:ReAnchorBars()
- self:ReAnchorLabels()
- self:UpdateBarLabelSettings()
- self:UpdateBarTextureSettings()
- self:UpdateClickThrough()
- self:UpdateRaidClassColors()
- self:UpdateFuBarSettings()
- -- These remainder settings were not placed in functions
- -- and were just updated directly from the config code.
- if LDBIcon and not IsAddOnLoaded("Broker2FuBar") and not IsAddOnLoaded("FuBar") then
- LDBIcon:Refresh("Omen", db.MinimapIcon)
- end
- if db.ShowWith.HideWhenOOC then
- self:RegisterEvent("PLAYER_REGEN_DISABLED", "UpdateVisible")
- self:RegisterEvent("PLAYER_REGEN_ENABLED", "UpdateVisible")
- else
- self:UnregisterEvent("PLAYER_REGEN_DISABLED")
- self:UnregisterEvent("PLAYER_REGEN_ENABLED")
- end
- if db.UseFocus then
- self:RegisterEvent("UNIT_TARGET")
- else
- self:UnregisterEvent("UNIT_TARGET")
- end
- local f = self.TPSUpdateFrame
- if f then
- if db.Bar.ShowTPS then f:Show() else f:Hide() end
- end
- if db.Bar.ShowValue and db.Bar.ShowPercent then
- bars[0].Text2:SetText(L["Threat [%]"])
- else
- bars[0].Text2:SetText(L["Threat"])
- end
-
- self:UpdateVisible()
- self:UpdateBars()
-end
-
-function Omen:UpdateUsedMedia(event, mediatype, key)
- if mediatype == "statusbar" then
- if key == db.Bar.Texture then self:UpdateBarTextureSettings() end
- elseif mediatype == "font" then
- if key == db.TitleBar.Font then self:UpdateTitleBar() end
- if key == db.Bar.Font then self:UpdateBarLabelSettings() self:UpdateBars() end
- elseif mediatype == "background" then
- if key == db.Background.Texture then self:UpdateBackdrop() end
- elseif mediatype == "border" then
- if key == db.Background.BorderTexture then self:UpdateBackdrop() end
- --elseif mediatype == "sound" then
- -- Do nothing
- end
-end
-
-function Omen:SetAnchors(useDB)
- local x, y, w, h
-
- -- Set the scale, since the scaling affects the position
- self.Anchor:SetScale(db.Scale)
- self.VGrip1:SetWidth(1 / self.VGrip1:GetEffectiveScale())
- self.VGrip2:SetWidth(1 / self.VGrip2:GetEffectiveScale())
-
- -- Get position
- if useDB then
- x, y = db.PositionX, db.PositionY
- if not x or not y then
- self.Anchor:ClearAllPoints()
- self.Anchor:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
- x, y = self.Anchor:GetLeft(), db.GrowUp and self.Anchor:GetBottom() or self.Anchor:GetTop()
- end
- else
- x, y = self.Anchor:GetLeft(), db.GrowUp and self.Anchor:GetBottom() or self.Anchor:GetTop()
- end
-
- -- Get width/height
- w = useDB and db.PositionW or self.Anchor:GetWidth()
- h = useDB and db.PositionH or self.Anchor:GetHeight()
-
- -- Set the anchors and size
- self.Anchor:ClearAllPoints()
- self.Anchor:SetPoint(db.GrowUp and "BOTTOMLEFT" or "TOPLEFT", UIParent, "BOTTOMLEFT", x, y)
- self.Anchor:SetWidth(w)
- self.Anchor:SetHeight(h)
- self.Anchor:SetUserPlaced(nil)
-
- -- Save the data
- db.PositionX, db.PositionY = x, y
- db.PositionW, db.PositionH = w, h
-end
-
--- For public use
-function Omen:Toggle(setting)
- -- Don't set the manualToggle flag if "Hide Omen on 0 bars" option is active
- if not (db.Autocollapse and db.CollapseHide) then
- manualToggle = true
- end
- return self:_toggle(setting)
-end
-
--- For internal use
-function Omen:_toggle(setting)
- if setting == nil then
- setting = not self.Anchor:IsShown()
- end
- if setting then
- self.Anchor:Show()
- else
- self.Anchor:Hide()
- end
-end
-
-function Omen:UpdateVisible(event)
- local t = db.ShowWith
- if not t.UseShowWith or manualToggle then return end
-
- -- Hide if HideWhenOOC option is on, we're not in combat, and the triggering event is not
- -- "PLAYER_REGEN_DISABLED" (we're out of combat during this event just before entering combat)
- if t.HideWhenOOC and not InCombatLockdown() and event ~= "PLAYER_REGEN_DISABLED" then
- self:_toggle(false)
- return
- end
-
- -- Check for pet|party|raid|alone
- local show = (t.Pet and UnitExists("pet")) or
- (t.Party and inParty) or
- (t.Raid and inRaid) or
- (t.Alone and not inParty and not inRaid and not UnitExists("pet"))
-
- -- Then hide override if necessary for resting|pvp
- local inInstance, instanceType = IsInInstance()
- if (t.HideWhileResting and IsResting()) or (t.HideInPVP and (instanceType == "pvp" or instanceType == "arena")) then
- show = false
- end
-
- -- Hide if Autocollapse and Hide Omen on 0 Bars are both active and there are 0 bars.
- if db.Autocollapse and db.CollapseHide and self.BarList.barsShown == 0 then
- show = false
- end
-
- self:_toggle(show)
-end
-
-local bgFrame = {insets = {}}
-function Omen:UpdateBackdrop()
- bgFrame.bgFile = LSM:Fetch("background", db.Background.Texture)
- bgFrame.edgeFile = LSM:Fetch("border", db.Background.BorderTexture)
- bgFrame.tile = db.Background.Tile
- bgFrame.tileSize = db.Background.TileSize
- bgFrame.edgeSize = db.Background.EdgeSize
- local inset = floor(db.Background.EdgeSize / 4)
- bgFrame.insets.left = inset
- bgFrame.insets.right = inset
- bgFrame.insets.top = inset
- bgFrame.insets.bottom = inset
- self.BarList:SetBackdrop(bgFrame)
- if not db.TitleBar.UseSameBG then
- bgFrame.bgFile = LSM:Fetch("background", db.TitleBar.Texture)
- bgFrame.edgeFile = LSM:Fetch("border", db.TitleBar.BorderTexture)
- bgFrame.tile = db.TitleBar.Tile
- bgFrame.tileSize = db.TitleBar.TileSize
- bgFrame.edgeSize = db.TitleBar.EdgeSize
- local inset = floor(db.TitleBar.EdgeSize / 4)
- bgFrame.insets.left = inset
- bgFrame.insets.right = inset
- bgFrame.insets.top = inset
- bgFrame.insets.bottom = inset
-
- end
- self.Title:SetBackdrop(bgFrame)
-
- local c = db.Background.Color
- self.BarList:SetBackdropColor(c.r, c.g, c.b, c.a)
- if not db.TitleBar.UseSameBG then c = db.TitleBar.Color end
- self.Title:SetBackdropColor(c.r, c.g, c.b, c.a)
-
- c = db.Background.BorderColor
- self.BarList:SetBackdropBorderColor(c.r, c.g, c.b, c.a)
- if not db.TitleBar.UseSameBG then c = db.TitleBar.BorderColor end
- self.Title:SetBackdropBorderColor(c.r, c.g, c.b, c.a)
-
- local h = db.Background.EdgeSize * 2
- if not db.TitleBar.UseSameBG then h = db.TitleBar.EdgeSize * 2 end
- self.Anchor:SetMinResize(90, h)
- self.Title:SetMinResize(90, h)
- if not db.TitleBar.ShowTitleBar then
- self.Title:SetHeight(1e-6) -- See comment in Omen:UpdateTitleBar()
- elseif h > db.TitleBar.Height then
- self.Title:SetHeight(h)
- else
- self.Title:SetHeight(db.TitleBar.Height)
- end
- if self.Options then
- self.Options.args.TitleBar.args.Height.min = h
- end
-
- --self.FocusButton:SetPoint("TOPRIGHT", -inset, -inset)
-
- self.BarList:ClearAllPoints() -- See comment in Omen:UpdateTitleBar()
- self.BarList:SetPoint("TOPLEFT", self.Title, "BOTTOMLEFT")
- self.BarList:SetPoint("BOTTOMRIGHT", self.Anchor, "BOTTOMRIGHT")
-
- self:ResizeBars()
- self:ReAnchorBars()
- self:UpdateBars()
-end
-
-function Omen:UpdateTitleBar()
- local font = LSM:Fetch("font", db.TitleBar.Font)
- local size = db.TitleBar.FontSize
- local flags = db.TitleBar.FontOutline
- local color = db.TitleBar.FontColor
- self.TitleText:SetFont(font, size, flags)
- self.TitleText:SetTextColor(color.r, color.g, color.b, color.a)
- local h = db.Background.EdgeSize * 2
- if not db.TitleBar.UseSameBG then h = db.TitleBar.EdgeSize * 2 end
- if not db.TitleBar.ShowTitleBar then
- -- Yes, its a hack, since it can't be set to 0
- self.Title:SetHeight(1e-6)
- self.Title:Hide()
- elseif h > db.TitleBar.Height then
- self.Title:SetHeight(h)
- self.Title:Show()
- else
- self.Title:SetHeight(db.TitleBar.Height)
- self.Title:Show()
- end
- -- This forces the UI to redraw it, I couldn't find a better way. Although it is
- -- anchored to the Title, it doesn't update automatically on the height change.
- self.BarList:ClearAllPoints()
- self.BarList:SetPoint("TOPLEFT", self.Title, "BOTTOMLEFT")
- self.BarList:SetPoint("BOTTOMRIGHT", self.Anchor, "BOTTOMRIGHT")
-end
-
-function Omen:UpdateFuBarSettings()
- if LibStub:GetLibrary("LibFuBarPlugin-3.0", true) then
- if db.FuBar.HideMinimapButton then
- self:Hide()
- else
- self:Show()
- if self:IsFuBarMinimapAttached() ~= db.FuBar.AttachMinimap then
- self:ToggleFuBarMinimapAttached()
- end
- end
- end
-end
-
-function Omen:UpdateGrips()
- self.VGrip1:ClearAllPoints()
- self.VGrip1:SetPoint("TOPLEFT", self.BarList, "TOPLEFT", db.VGrip1, 0)
- self.VGrip1:SetPoint("BOTTOMLEFT", self.BarList, "BOTTOMLEFT", db.VGrip1, 0)
- self.VGrip2:ClearAllPoints()
- self.VGrip2:SetPoint("TOPLEFT", self.BarList, "TOPLEFT", db.VGrip2, 0)
- self.VGrip2:SetPoint("BOTTOMLEFT", self.BarList, "BOTTOMLEFT", db.VGrip2, 0)
- if db.Locked then
- self.Grip:Hide()
- self.VGrip1:Hide()
- self.VGrip2:Hide()
- else
- self.Grip:Show()
- self.VGrip1:Show()
- if db.Bar.ShowTPS then
- self.VGrip2:Show()
- else
- self.VGrip2:Hide()
- end
- end
-end
-
-function Omen:ToggleFocus()
- db.UseFocus = not db.UseFocus
- if db.UseFocus then
- Omen:RegisterEvent("UNIT_TARGET")
- else
- Omen:UnregisterEvent("UNIT_TARGET")
- end
- Omen:UpdateBars()
-end
-
-function Omen:UpdateRaidClassColors()
- if CUSTOM_CLASS_COLORS and db.Bar.UseCustomClassColors then
- RAID_CLASS_COLORS = CUSTOM_CLASS_COLORS
- else
- RAID_CLASS_COLORS = _G.RAID_CLASS_COLORS
- end
- Omen:UpdateBars()
-end
-
-
------------------------------------------------------------------------------
--- Omen warnings
-
-function Omen:Flash()
- if not self.FlashFrame then
- local flasher = CreateFrame("Frame", "OmenFlashFrame")
- flasher:SetToplevel(true)
- flasher:SetFrameStrata("FULLSCREEN_DIALOG")
- flasher:SetAllPoints(UIParent)
- flasher:EnableMouse(false)
- flasher:Hide()
- flasher.texture = flasher:CreateTexture(nil, "BACKGROUND")
- flasher.texture:SetTexture("Interface\\FullScreenTextures\\LowHealth")
- flasher.texture:SetAllPoints(UIParent)
- flasher.texture:SetBlendMode("ADD")
- flasher:SetScript("OnShow", function(self)
- self.elapsed = 0
- self:SetAlpha(0)
- end)
- flasher:SetScript("OnUpdate", function(self, elapsed)
- elapsed = self.elapsed + elapsed
- if elapsed < 2.6 then
- local alpha = elapsed % 1.3
- if alpha < 0.15 then
- self:SetAlpha(alpha / 0.15)
- elseif alpha < 0.9 then
- self:SetAlpha(1 - (alpha - 0.15) / 0.6)
- else
- self:SetAlpha(0)
- end
- else
- self:Hide()
- end
- self.elapsed = elapsed
- end)
- self.FlashFrame = flasher
- end
-
- self.FlashFrame:Show()
-end
-
--- This function is adapted from Omen2 to be self-contained,
--- which was initially taken from BigWigs
-function Omen:Shake()
- local shaker = self.ShakerFrame
- if not shaker then
- shaker = CreateFrame("Frame", "OmenShaker", UIParent)
- shaker:Hide()
- shaker:SetScript("OnUpdate", function(self, elapsed)
- elapsed = self.elapsed + elapsed
- local x, y = 0, 0 -- Resets to original position if we're supposed to stop.
- if elapsed >= 0.8 then
- self:Hide()
- else
- x, y = random(-8, 8), random(-8, 8)
- end
- if WorldFrame:IsProtected() and InCombatLockdown() then
- if not shaker.fail then
- Omen:Print(L["|cffff0000Error:|r Omen cannot use shake warning if you have turned on nameplates at least once since logging in."])
- shaker.fail = true
- end
- self:Hide()
- else
- WorldFrame:ClearAllPoints()
- for i = 1, #self.originalPoints do
- local v = self.originalPoints[i]
- WorldFrame:SetPoint(v[1], v[2], v[3], v[4] + x, v[5] + y)
- end
- end
- self.elapsed = elapsed
- end)
- shaker:SetScript("OnShow", function(self)
- -- Store old worldframe positions, we need them all, people have frame modifiers for it
- if not self.originalPoints then
- self.originalPoints = {}
- for i = 1, WorldFrame:GetNumPoints() do
- tinsert(self.originalPoints, {WorldFrame:GetPoint(i)})
- end
- end
- self.elapsed = 0
- end)
- self.ShakerFrame = shaker
- end
-
- shaker:Show()
-end
-
-function Omen:Warn(sound, flash, shake, message)
- if sound then PlaySoundFile(LSM:Fetch("sound", db.Warnings.SoundFile)) end
- if flash then self:Flash() end
- if shake then self:Shake() end
- if message then self:Pour(message, 1, 0, 0, nil, 24, "OUTLINE", true) end
-end
-
-
------------------------------------------------------------------------------
--- Omen bar stuff
-
-do
- -- OnUpdate function for bar animation, lasts 0.25 seconds
- local function animate(self, elapsed)
- local t = self.animationCursor + elapsed
- local animData = self.animData
- if t >= 0.25 then
- self.texture:SetWidth(animData[1])
- animData[3] = nil
- animData[2] = nil
- animData[1] = nil
- t = 0
- self:SetScript("OnUpdate", nil)
- else
- self.texture:SetWidth(animData[2] + animData[3] * t / 0.25)
- end
- self.animationCursor = t
- end
-
- -- function to start bar animations
- local function AnimateTo(self, val)
- if val == 1/0 or val == -1/0 then return end -- infinity, do nothing
- if val == 0 then val = 1 end -- at least 1 pixel width
- local animData = self.animData
- if animData[1] == val then return end -- there is already an animation to the target width
- local currentWidth = self.texture:GetWidth()
- --if currentWidth > self:GetWidth() then currentWidth = self:GetWidth() end
- if val == currentWidth then return end -- the current width is already the target width
- animData[1] = val
- animData[2] = currentWidth
- animData[3] = val - currentWidth
- self.animationCursor = 0
- self:SetScript("OnUpdate", animate)
- end
-
- -- Create bars on demand
- setmetatable(bars, {__index = function(self, barID)
- local bar = CreateFrame("Frame", nil, Omen.BarList)
- self[barID] = bar
-
- local inset = db.Background.BarInset
- local inset2 = db.Background.BarInset * 2
- local color = db.Bar.InvertColors and db.Bar.BarColor or db.Bar.FontColor
-
- bar:SetWidth(Omen.Anchor:GetWidth() - inset2)
- bar:SetHeight(db.Bar.Height)
- if db.Bar.ShowHeadings then
- bar:SetPoint("TOPLEFT", Omen.BarList, "TOPLEFT", inset, -inset + (barID) * -(db.Bar.Height + db.Bar.Spacing))
- else
- bar:SetPoint("TOPLEFT", Omen.BarList, "TOPLEFT", inset, -inset + (barID-1) * -(db.Bar.Height + db.Bar.Spacing))
- end
-
- bar.Text1 = bar:CreateFontString(nil, nil, "GameFontNormalSmall")
- bar.Text1:SetPoint("LEFT", bar, "LEFT", 5, 1)
- bar.Text1:SetJustifyH("LEFT")
- bar.Text1:SetFont(LSM:Fetch("font", db.Bar.Font), db.Bar.FontSize, db.Bar.FontOutline)
- bar.Text1:SetTextColor(color.r, color.g, color.b, color.a)
- bar.Text1:SetWidth(db.VGrip1 - 5)
- bar.Text1:SetHeight(db.Bar.FontSize)
- bar.Text1:SetNonSpaceWrap(false)
-
- bar.Text2 = bar:CreateFontString(nil, nil, "GameFontNormalSmall")
- bar.Text2:SetPoint("RIGHT", bar, "RIGHT", -5, 1)
- bar.Text2:SetJustifyH("RIGHT")
- bar.Text2:SetFont(LSM:Fetch("font", db.Bar.Font), db.Bar.FontSize, db.Bar.FontOutline)
- bar.Text2:SetTextColor(color.r, color.g, color.b, color.a)
- if db.Bar.ShowTPS then
- bar.Text2:SetWidth(Omen.BarList:GetWidth() - db.VGrip2 - 5)
- else
- bar.Text2:SetWidth(Omen.BarList:GetWidth() - db.VGrip1 - 5)
- end
- bar.Text2:SetHeight(db.Bar.FontSize)
- bar.Text2:SetNonSpaceWrap(false)
-
- bar.Text3 = bar:CreateFontString(nil, nil, "GameFontNormalSmall")
- bar.Text3:SetPoint("LEFT", bar.Text1, "RIGHT", 0, 0)
- bar.Text3:SetJustifyH("RIGHT")
- bar.Text3:SetFont(LSM:Fetch("font", db.Bar.Font), db.Bar.FontSize, db.Bar.FontOutline)
- bar.Text3:SetTextColor(color.r, color.g, color.b, color.a)
- bar.Text3:SetWidth(db.VGrip2 - db.VGrip1 - 5)
- bar.Text3:SetHeight(db.Bar.FontSize)
- bar.Text3:SetNonSpaceWrap(false)
- if not db.Bar.ShowTPS then bar.Text3:Hide() end
-
- bar.texture = bar:CreateTexture()
- bar.texture:SetTexture(LSM:Fetch("statusbar", db.Bar.Texture))
- bar.texture:SetPoint("TOPLEFT", bar, "TOPLEFT")
- bar.texture:SetPoint("BOTTOMLEFT", bar, "BOTTOMLEFT")
- color = db.Bar.InvertColors and db.Bar.FontColor or db.Bar.BarColor
- bar.texture:SetVertexColor(color.r, color.g, color.b, color.a)
-
- bar.animData = {}
- bar.animationCursor = 0
- bar.AnimateTo = AnimateTo
-
- if barID == 0 then
- bar.Text1:SetText(L["Name"])
- if db.Bar.ShowValue and db.Bar.ShowPercent then
- bar.Text2:SetText(L["Threat [%]"])
- else
- bar.Text2:SetText(L["Threat"])
- end
- bar.Text3:SetText(L["TPS"])
- color = db.Bar.InvertColors and db.Bar.FontColor or db.Bar.HeadingBGColor
- bar.texture:SetVertexColor(color.r, color.g, color.b, color.a)
- bar:Hide()
- elseif barID == 1 then
- -- Parent our TPS update frame to the first bar, so that TPS updates
- -- updates happen when at least 1 bar (the first bar) is shown.
- Omen.TPSUpdateFrame = CreateFrame("Frame", nil, bar)
- Omen.TPSUpdateFrame:SetScript("OnUpdate", function(self, elapsed) Omen:UpdateTPS() end)
- if not db.Bar.ShowTPS then Omen.TPSUpdateFrame:Hide() end
- end
-
- return bar
- end})
-end
-
-function Omen:ResizeBars()
- local inset = db.Background.BarInset * 2
- local w = Omen.Anchor:GetWidth() - inset
- for i = 0, #bars do
- bars[i]:SetWidth(w)
- bars[i]:SetHeight(db.Bar.Height)
- end
-end
-
-function Omen:ReAnchorBars()
- local inset = db.Background.BarInset
- if db.Bar.ShowHeadings then
- for i = 0, #bars do
- bars[i]:SetPoint("TOPLEFT", self.BarList, "TOPLEFT", inset, -inset + (i) * -(db.Bar.Height + db.Bar.Spacing))
- end
- else
- for i = 1, #bars do
- bars[i]:SetPoint("TOPLEFT", self.BarList, "TOPLEFT", inset, -inset + (i-1) * -(db.Bar.Height + db.Bar.Spacing))
- end
- bars[0]:Hide()
- end
-end
-
-function Omen:UpdateBarLabelSettings()
- local font = LSM:Fetch("font", db.Bar.Font)
- local size = db.Bar.FontSize
- local flags = db.Bar.FontOutline
- local color = db.Bar.InvertColors and db.Bar.BarColor or db.Bar.FontColor
- local color2 = db.Bar.InvertColors and db.Bar.FontColor or db.Bar.BarColor
- for i = 0, #bars do
- bars[i].Text1:SetFont(font, size, flags)
- bars[i].Text2:SetFont(font, size, flags)
- bars[i].Text3:SetFont(font, size, flags)
- bars[i].Text1:SetTextColor(color.r, color.g, color.b, color.a)
- bars[i].Text2:SetTextColor(color.r, color.g, color.b, color.a)
- bars[i].Text3:SetTextColor(color.r, color.g, color.b, color.a)
- bars[i].Text1:SetHeight(size)
- bars[i].Text2:SetHeight(size)
- bars[i].Text3:SetHeight(size)
- bars[i].texture:SetVertexColor(color2.r, color2.g, color2.b, color2.a)
- end
- color = db.Bar.InvertColors and db.Bar.FontColor or db.Bar.HeadingBGColor
- color2 = db.Bar.InvertColors and db.Bar.HeadingBGColor or db.Bar.FontColor
- bars[0].texture:SetVertexColor(color.r, color.g, color.b, color.a)
- bars[0].Text1:SetTextColor(color2.r, color2.g, color2.b, color2.a)
- bars[0].Text2:SetTextColor(color2.r, color2.g, color2.b, color2.a)
- bars[0].Text3:SetTextColor(color2.r, color2.g, color2.b, color2.a)
-end
-
-function Omen:ReAnchorLabels()
- local w = db.VGrip1
- local w2 = db.Bar.ShowTPS and Omen.BarList:GetWidth() - db.VGrip2 or Omen.BarList:GetWidth() - w
- local w3 = db.VGrip2 - db.VGrip1
- for i = 0, #bars do
- bars[i].Text1:SetWidth(w - 5)
- bars[i].Text2:SetWidth(w2 - 5)
- if db.Bar.ShowTPS then
- bars[i].Text3:SetWidth(w3 - 5)
- bars[i].Text3:Show()
- else
- bars[i].Text3:Hide()
- end
- end
-end
-
-function Omen:UpdateBarTextureSettings()
- local texturepath = LSM:Fetch("statusbar", db.Bar.Texture)
- for i = 0, #bars do
- bars[i].texture:SetTexture(texturepath)
- end
-end
-
-function Omen:UpdateClickThrough()
- self.Title:EnableMouse(not db.ClickThrough)
- self.BarList:EnableMouse(not db.ClickThrough)
-end
-
-
------------------------------------------------------------------------------
--- Omen event functions
-
--- Fired when a mob has its threat list updated. The mob that
--- had its list updated is the first parameter of the event.
-function Omen:UNIT_THREAT_LIST_UPDATE(event, unitID)
- -- It appears that unitID can only be "target" or "focus"
- self:UpdateBars()
-end
-
--- Fired when a unit's threat situation changes. The unit that
--- had a change in threat situation is the first parameter of
--- the event. Note that this only triggers when major state
--- changes, not when the raw threat values change.
-function Omen:UNIT_THREAT_SITUATION_UPDATE(...)
- self:UpdateBars()
-end
-
-function Omen:PLAYER_TARGET_CHANGED()
- -- Stop our unit update timer for updating threat on "targettarget"
- if timers.UpdateBars then
- self:CancelTimer(timers.UpdateBars, true)
- timers.UpdateBars = nil
- end
- self:UpdateBars()
-end
-
-function Omen:UNIT_TARGET(event, unitID)
- if unitID == "focus" and db.UseFocus and self.unitID == "focustarget" then
- self:UpdateBars()
- end
-end
-
-local lastPartyUpdateTime = GetTime()
-
-function Omen:PARTY_MEMBERS_CHANGED()
- local oldInParty, oldInRaid = inParty, inRaid
- inParty = GetNumPartyMembers() > 0
- inRaid = GetNumRaidMembers() > 0
- if oldInParty ~= inParty or oldInRaid ~= inRaid then manualToggle = false end
- self:UpdateVisible()
-
- -- Run the update if the last call is more than 0.5 seconds ago else
- -- schedule an update 0.5 seconds later if one isn't already scheduled
- if GetTime() - lastPartyUpdateTime > 0.5 then
- self:UpdatePartyGUIDs()
- elseif not timers.UpdatePartyGUIDs then
- timers.UpdatePartyGUIDs = self:ScheduleTimer("UpdatePartyGUIDs", 0.5)
- end
-end
-
--- This function updates the name and class guid lookup tables of the raid
-function Omen:UpdatePartyGUIDs()
- lastPartyUpdateTime = GetTime()
- if timers.UpdatePartyGUIDs then
- self:CancelTimer(timers.UpdatePartyGUIDs, true)
- timers.UpdatePartyGUIDs = nil
- end
-
- local _
- local me = UnitGUID("player")
- wipe(guidClassLookup)
- if me then -- Because it sometimes is nil on zoning/logging in.
- guidNameLookup[me] = UnitName("player")
- _, guidClassLookup[me] = UnitClass("player")
- else
- timers.UpdatePartyGUIDs = self:ScheduleTimer("UpdatePartyGUIDs", 0.5)
- end
- if UnitExists("pet") then
- local petGUID = UnitGUID("pet")
- guidClassLookup[petGUID] = "PET"
- guidNameLookup[petGUID] = UnitName("pet")--.." ["..UnitName("player").."]"
- end
-
- if inParty or inRaid then
- local playerFmt = inRaid and rID or pID
- local petFmt = inRaid and rpID or ppID
- local currentPartySize = inRaid and GetNumRaidMembers() or GetNumPartyMembers()
-
- for i = 1, currentPartySize do
- local unitID = playerFmt[i]
- local pGUID = UnitGUID(unitID)
-
- if pGUID then
- guidNameLookup[pGUID] = UnitName(unitID)
- _, guidClassLookup[pGUID] = UnitClass(unitID)
-
- -- lookup pet (if existing)
- local petID = petFmt[i]
- local petGUID = UnitGUID(petID)
- if petGUID then
- guidNameLookup[petGUID] = UnitName(petID)--.." ["..UnitName(unitID).."]"
- guidClassLookup[petGUID] = "PET"
- end
- end
- end
- end
- guidNameLookup["AGGRO"] = L["> Pull Aggro <"]
- guidClassLookup["AGGRO"] = "AGGRO"
-end
-
-function Omen:PLAYER_ENTERING_WORLD()
- manualToggle = false
- wipe(guidNameLookup)
- self:PARTY_MEMBERS_CHANGED()
-end
-
-
------------------------------------------------------------------------------
--- Omen update functions
-
---[[
-First, some definitions:
-* mob - enemy creature
-* threat list - a mob's list of possible targets, along with each possible target's current threat value
-* threat situation - the situation that a unit is currently in (either globally, or with respect to a certain mob)
-* scaled percentage - a threat percentage, where 100% means you will pull aggro (become the primary target of the mob), and thus this % cannot be higher than 100% under normal circumstances
-* raw threat percentage - the percentage of the units threat when divided by the threat of the mob's current primary target, this % CAN be over 100%
----------
-state = UnitThreatSituation(unit, mob)
-
-Returns the unit's threat situation with respect to the given mob. The state can be one of the following values:
-nil = the unit is not on the mob's threat list
-0 = 0-99% raw threat percentage (no indicator shown)
-1 = 100% or more raw threat percentage (yellow warning indicator shown)
-2 = tanking, other has 100% or more raw threat percentage (orange indicator shown)
-3 = tanking, all others have less than 100% raw percentage threat (red indicator shown)
----------
-state = UnitThreatSituation(unit)
-
-Returns the unit's maximum threat state on any mob's threat list.
----------
-isTanking, state, scaledPercent, rawPercent, threatValue = UnitDetailedThreatSituation(unit, mob)
-
-Returns detailed information about the unit's state on the mob's threat list.
-isTanking is true if the unit the primary target of the mob (and by definition has 100% threat)
-state is the unit's threat situation, as listed above.
-scaledPercent is the current percent threat of the unit, scaled in the 0-100% range based on distance from target.
-rawPercent is the current percent threat of the unit relative to the primary target of the mob.
-threatValue is the amount of threat that the unit has on the mob's threat list. This is roughly approximate to the amount of damage and healing the unit has done.
----------
-r, g, b = GetThreatStatusColor(state)
-
-Returns the colors used in the UI to represent each major threat state.
-]]
-
-local threatTable -- Format: threatTable[guid] = threatValue
-local sortTable = {} -- Format: threatTable[i] = guid -- used for sorting by sortfunction()
-local tankGUID -- Used to store which unit is tanking and hence has 100% threat by definition
-local topthreat -- Used to store the top threat value
-local lastWarn = { -- Used to store information for threat warnings
- threatpercent = 0,
-}
-local threatStore = {} -- Format: threatStore[i] = threatTable[guid] -- used for storing past threatTables
-local threatStoreTime = {} -- Format: threatStoreTime[i] = GetTime()
-local negativeTable = {} -- Format: negativeTable[guid] = true -- stores if someone is under the effects of Fade or Mirror Image
-
-local function sortfunction(a, b)
- return threatTable[a] > threatTable[b]
-end
-
-local function updatethreat(unitid, mobunitid)
- local guid = UnitGUID(unitid)
- if guid and not threatTable[guid] then
- local isTanking, state, scaledPercent, rawPercent, threatValue = UnitDetailedThreatSituation(unitid, mobunitid)
- if threatValue then
- -- Threat can be negative due to temporary threat reduction effects such as Fade and Mirror Image (-410065408).
- if threatValue < 0 then
- threatValue = threatValue + 410065408
- negativeTable[guid] = true
- end
- if threatValue > topthreat then topthreat = threatValue end
- if isTanking then tankGUID = guid end
- threatTable[guid] = threatValue
- else
- -- We use the special value -1 to indicate nil here.
- threatTable[guid] = -1
- end
- end
-end
-
-local threatUnitIDFindList = {"target", "targettarget"}
-local threatUnitIDFindList2 = {"focus", "focustarget", "target", "targettarget"}
-function Omen:FindThreatMob()
- -- Figure out which mob to show threat on.
- -- It has to be attackable and not human controlled.
- local t = db.UseFocus and threatUnitIDFindList2 or threatUnitIDFindList
- local name, name2
- for i = 1, #t do
- local mob = t[i]
- if UnitExists(mob) then
- name2 = UnitName(mob)
- guidNameLookup[UnitGUID(mob)] = name2
- if not name then name = name2 end
- if not UnitIsPlayer(mob) and UnitCanAttack("player", mob) and UnitHealth(mob) > 0 then
- if not db.IgnorePlayerPets or not UnitPlayerControlled(mob) then
- self.TitleText:SetText(name2)
- self.unitID = mob
- return mob
- end
- end
- end
- end
- self.TitleText:SetText(name)
- self.unitID = nil
-end
-
--- Frame for throtling updates
-local OmenUpdateBarsThrotleFrame = CreateFrame("Frame")
-OmenUpdateBarsThrotleFrame:Hide()
-OmenUpdateBarsThrotleFrame:SetScript("OnUpdate", function(self, elapsed)
- self:Hide()
- Omen:UpdateBarsReal()
-end)
-function Omen:UpdateBars()
- OmenUpdateBarsThrotleFrame:Show()
-end
-
-local queried = false
-function Omen:UpdateBarsReal()
- if db.Autocollapse and db.CollapseHide then
- -- Update the visibility because it could have been hidden on 0 bars
- self.BarList.barsShown = 1 -- Dummy value
- self:UpdateVisible()
- end
- if not self.Anchor:IsShown() then
- self.BarList.barsShown = 0
- return
- end
-
- local myGUID = UnitGUID("player")
- local dbBar = db.Bar
- local mob, mobGUID, mobTargetGUID
- topthreat = -1
-
- if testMode then
- threatTable = newTable()
- local key = next(showClassesOptionTable)
- for i = 1, 25 do
- if i == 22 and myGUID then -- Because I've got myGUID == nil before
- threatTable[myGUID] = i*5000
- else
- threatTable[i] = i*5000
- guidNameLookup[i] = showClassesOptionTable[key]
- if key ~= "*NOTINPARTY*" then guidClassLookup[i] = key end
- key = next(showClassesOptionTable, key) or next(showClassesOptionTable)
- end
- end
- tankGUID = 25
- topthreat = 25*5000
- mob = ""
- self.TitleText:SetText(L["Test Mode"])
- else
- mob = self:FindThreatMob()
- if not mob then
- self:ClearAll()
- return
- end
- mobGUID = UnitGUID(mob)
-
- -- Schedule a repeating timer for updating threat on "targettarget"
- -- since we get no events on a targettarget change.
- if mob == "targettarget" and not timers.UpdateBars then
- timers.UpdateBars = self:ScheduleRepeatingTimer("UpdateBars", 0.5)
- end
-
- -- We want the mob's target just in case the tank isn't
- -- in our raid (say an NPC or some other player)
- local mobTarget = mob.."target"
- local mobTargetGUID = UnitGUID(mobTarget)
- if mobTargetGUID then
- guidNameLookup[mobTargetGUID] = UnitName(mobTarget)
- end
-
- threatTable = newTable()
- threatTable[mobGUID] = -1
- tankGUID = nil
- wipe(negativeTable)
-
- -- Get data for threat on mob by scanning the whole raid
- if inParty or inRaid then
- if inRaid then
- for i = 1, GetNumRaidMembers() do
- updatethreat(rID[i], mob)
- updatethreat(rpID[i], mob)
- updatethreat(rtID[i], mob)
- updatethreat(rptID[i], mob)
- end
- else
- for i = 1, GetNumPartyMembers() do
- updatethreat(pID[i], mob)
- updatethreat(ppID[i], mob)
- updatethreat(ptID[i], mob)
- updatethreat(pptID[i], mob)
- end
- end
-
- end
- if not inRaid then
- updatethreat("player", mob)
- updatethreat("pet", mob)
- updatethreat("target", mob)
- updatethreat("pettarget", mob)
- end
- updatethreat("target", mob)
- updatethreat("targettarget", mob)
- updatethreat("focus", mob)
- updatethreat("focustarget", mob)
- updatethreat(mobTarget, mob)
- updatethreat("mouseover", mob)
- updatethreat("mouseovertarget", mob)
- end
- local tankThreat = tankGUID and threatTable[tankGUID] or mobTargetGUID and threatTable[mobTargetGUID] or topthreat
- if dbBar.ShowAggroBar and tankThreat > 0 then
- if GetItemInfo(37727) then -- 5 yards (Ruby Acorn - http://www.wowhead.com/?item=37727)
- threatTable["AGGRO"] = tankThreat * (IsItemInRange(37727, mob) == 1 and 1.1 or 1.3)
- else -- 9 yards compromise
- threatTable["AGGRO"] = tankThreat * (CheckInteractDistance(mob, 3) and 1.1 or 1.3)
- if not queried and not ItemRefTooltip:IsVisible() then
- ItemRefTooltip:SetHyperlink("item:37727")
- queried = true -- Only query once per session
- end
- end
- end
-
- -- Sort the threatTable
- local i = 1
- for k, v in pairs(threatTable) do
- if v ~= -1 then
- sortTable[i] = k
- i = i + 1
- end
- end
- for j = i, #sortTable do
- sortTable[j] = nil
- end
- if #sortTable == 0 then
- self:ClearAll()
- self.TitleText:SetText(guidNameLookup[mobGUID])
- return
- end
- sort(sortTable, sortfunction)
-
- -- Now update the bars on screen
- local inset = db.Background.BarInset * 2
- local w = self.BarList:GetWidth() - inset
- local h = self.BarList:GetHeight() - inset
- topthreat = threatTable[sortTable[1]]
- if topthreat == 0 then topthreat = 1 end -- To avoid 0/0 division
- local showSelfYet = true
-
- if dbBar.AlwaysShowSelf then
- -- Check if we're one of the bars to be displayed
- for j = 1, #sortTable do
- if sortTable[j] == myGUID then
- showSelfYet = false -- Yes, so flag it false
- break
- end
- end
- end
-
- -- Check how many bars of space we have
- local numBars = db.Autocollapse and db.NumBars or floor((h - dbBar.Height) / (dbBar.Height + dbBar.Spacing) + 1.01)
-
- i = 1 -- Counts one higher than number of bars used
- if dbBar.ShowHeadings then
- if i <= numBars then
- i = i + 1
- bars[0].texture:SetWidth(w)
- bars[0]:Show()
- end
- else
- bars[0]:Hide()
- end
- for j = 1, #sortTable do
- if i > numBars then break end
- local guid = sortTable[j]
- local class = guidClassLookup[guid]
- local show = class == nil and dbBar.Classes["*NOTINPARTY*"] or class == "AGGRO" and dbBar.ShowAggroBar or dbBar.Classes[class]
- if dbBar.AlwaysShowSelf and i == numBars and not showSelfYet and guid ~= myGUID then
- show = false
- end
- if dbBar.AlwaysShowSelf and guid == myGUID then
- show = true
- showSelfYet = true
- end
- if show then
- local bar = bars[dbBar.ShowHeadings and i-1 or i]
- local threat = threatTable[guid]
-
- -- Update the text on the bar
- bar.Text1:SetText(guidNameLookup[guid])
- if dbBar.ShowPercent and dbBar.ShowValue then
- if dbBar.ShortNumbers and threat >= 100000 then
- bar.Text2:SetFormattedText("%2.1fk [%d%%]", threat / 100000, tankThreat == 0 and 0 or threat / tankThreat * 100)
- else
- bar.Text2:SetFormattedText("%d [%d%%]", threat / 100, tankThreat == 0 and 0 or threat / tankThreat * 100)
- end
- elseif dbBar.ShowValue then
- if dbBar.ShortNumbers and threat >= 100000 then
- bar.Text2:SetFormattedText("%2.1fk", threat / 100000)
- else
- bar.Text2:SetFormattedText("%d", threat / 100)
- end
- else
- bar.Text2:SetFormattedText("%d%%", tankThreat == 0 and 0 or threat / tankThreat * 100)
- end
-
- -- Update the color of the bar
- local c = (negativeTable[guid] and dbBar.FadeBarColor) or
- (guid == myGUID and dbBar.UseMyBarColor and dbBar.MyBarColor) or
- (guid == tankGUID and dbBar.UseTankBarColor and dbBar.TankBarColor) or
- (guid == "AGGRO" and dbBar.AggroBarColor) or
- (dbBar.UseClassColors and (RAID_CLASS_COLORS[class] or (class == "PET" and dbBar.PetBarColor))) or
- dbBar.BarColor
- if dbBar.InvertColors then
- bar.Text1:SetTextColor(c.r, c.g, c.b, c.a or 1)
- bar.Text2:SetTextColor(c.r, c.g, c.b, c.a or 1)
- bar.Text3:SetTextColor(c.r, c.g, c.b, c.a or 1)
- else
- bar.texture:SetVertexColor(c.r, c.g, c.b, c.a or 1)
- end
-
- -- Update the width of the bar, and animate if necessary
- local width = w * threat / topthreat
- if width <= 0 then width = 1 end
- if dbBar.AnimateBars and self.Anchor.IsMovingOrSizing ~= 2 then
- bar:AnimateTo(width)
- else
- bar.texture:SetWidth(width)
- end
-
- bar.guid = guid -- For TPS calcs
- bar:Show()
- i = i + 1
- end
- end
- -- And hide the rest
- for j = dbBar.ShowHeadings and i-1 or i, #bars do
- bars[j]:Hide()
- end
- if db.Autocollapse then
- self.Anchor:SetHeight((i-1)*dbBar.Height + (i-2)*dbBar.Spacing + self.Title:GetHeight() + inset)
- end
- self.BarList:Show()
- self.BarList.barsShown = dbBar.ShowHeadings and i-2 or i-1
-
- -- Threat warnings
- if testMode then
- threatTable = delTable(threatTable)
- elseif myGUID then
- local myClass = guidClassLookup[myGUID]
- local myThreatPercent = threatTable[myGUID] / tankThreat * 100
- local t = db.Warnings
- if lastWarn.mobGUID == mobGUID and myThreatPercent >= t.Threshold and t.Threshold > lastWarn.threatpercent then
- if not t.DisableWhileTanking or not (myClass == "WARRIOR" and GetBonusBarOffset() == 2 or
- myClass == "DRUID" and GetBonusBarOffset() == 3 or
- myClass == "PALADIN" and UnitAura("player", GetSpellInfo(25780)) or
- myClass == "DEATHKNIGHT" and GetShapeshiftForm() ~= 0 and GetShapeshiftFormInfo(GetShapeshiftForm()) == "Interface\\Icons\\Spell_Deathknight_FrostPresence") then
- self:Warn(t.Sound, t.Flash, t.Shake, t.Message and L["Passed %s%% of %s's threat!"]:format(t.Threshold, guidNameLookup[lastWarn.tankGUID]))
- end
- end
- -- Remove TPS data if the last scanned mob is different
- if lastWarn.mobGUID ~= mobGUID then
- delTable(threatStore)
- threatStore = newTable()
- wipe(threatStoreTime)
- end
- tinsert(threatStore, threatTable)
- tinsert(threatStoreTime, GetTime())
- -- Store last scanned mob GUID
- local u = tankGUID or mobTargetGUID or (dbBar.ShowAggroBar and sortTable[2] or sortTable[1])
- if u ~= "AGGRO" then
- lastWarn.mobGUID = mobGUID
- lastWarn.tankGUID = u
- lastWarn.threatpercent = myThreatPercent
- end
- threatTable = nil
- end
-end
-
-function Omen:ClearAll()
- for i = 0, #bars do
- bars[i]:Hide()
- end
- self.TitleText:SetText(self.defaultTitle)
- if db.Autocollapse then
- self.Anchor:SetHeight(self.Title:GetHeight())
- self.BarList:Hide()
- if db.CollapseHide and not self.Anchor.IsMovingOrSizing and not manualToggle then
- self.Anchor:Hide()
- end
- end
- self.BarList.barsShown = 0
- -- Store last scanned mob GUID
- lastWarn.mobGUID = nil
- lastWarn.tankGUID = nil
- lastWarn.threatpercent = 0
- -- Remove TPS data
- delTable(threatStore)
- threatStore = newTable()
- wipe(threatStoreTime)
- threatTable = nil
-end
-
-function Omen:UpdateTPS()
- local numBars = #bars
- if testMode then
- if db.Bar.ShowAggroBar then
- bars[1].Text3:SetText("--")
- for i = 2, numBars do
- bars[i].Text3:SetText(1300 - 50*(i-1))
- end
- else
- for i = 1, numBars do
- bars[i].Text3:SetText(1300 - 50*i)
- end
- end
- return
- end
- -- Remove data that is too old
- local TPSWindow = db.Bar.TPSWindow
- local startTime = GetTime() - TPSWindow
- while threatStoreTime[2] and startTime > threatStoreTime[2] do
- delTable(tremove(threatStore, 1))
- tremove(threatStoreTime, 1)
- end
- -- Now check that we still have enough data
- local dataSize = #threatStoreTime
- if dataSize == 0 or startTime <= threatStoreTime[1] then
- -- We do not have enough data, TPSWindow seconds has not passed
- for i = 1, numBars do
- bars[i].Text3:SetText("??")
- end
- return
- end
- -- Check for special case with just 1 data point past TPSWindow seconds
- if dataSize == 1 then
- -- Threat generated is 0
- for i = 1, numBars do
- bars[i].Text3:SetText("0")
- end
- return
- end
- -- We have at least 2 data points
- for i = 1, numBars do
- local bar = bars[i]
- if not bar:IsShown() then return end
- local guid = bar.guid
- if guid == "AGGRO" then
- bar.Text3:SetText("--")
- else
- local baseThreat = threatStore[1][guid]
- local secondThreat = threatStore[2][guid]
- local finalThreat = threatStore[dataSize][guid]
- if baseThreat and secondThreat and finalThreat then
- -- Calculate TPS
- local ratio = (startTime - threatStoreTime[1]) / (threatStoreTime[2] - threatStoreTime[1])
- local startThreat = (secondThreat - baseThreat) * ratio + baseThreat
- bar.Text3:SetFormattedText("%d", (finalThreat - startThreat) / TPSWindow / 100)
- else
- -- We don't have enough data for this unit
- bar.Text3:SetText("??")
- end
- end
- end
-end
-
-
------------------------------------------------------------------------------
--- Title Right Click menu
-
-do
- -- Upvalue the functions in the menu
- local function updateGrip()
- db.Locked = not db.Locked
- Omen:UpdateGrips()
- LibStub("AceConfigRegistry-3.0"):NotifyChange("Omen")
- end
- local function toggleFocus() Omen:ToggleFocus() end
- local function toggleTestMode()
- testMode = not testMode
- Omen:UpdateBars()
- LibStub("AceConfigRegistry-3.0"):NotifyChange("Omen")
- end
- local function showConfig() Omen:ShowConfig() end
- local function toggle() Omen:Toggle() end
-
- function Omen.TitleQuickMenu(self, level)
- if not level then return end
- local info = self.info
- wipe(info)
- if level == 1 then
- -- Create the title of the menu
- info.isTitle = 1
- info.text = L["Omen Quick Menu"]
- info.notCheckable = 1
- UIDropDownMenu_AddButton(info, level)
-
- info.disabled = nil
- info.isTitle = nil
- info.notCheckable = nil
-
- info.text = L["Lock Omen"]
- info.func = updateGrip
- info.checked = db.Locked
- info.tooltipTitle = L["Lock Omen"]
- info.tooltipText = L["Locks Omen in place and prevents it from being dragged or resized."]
- UIDropDownMenu_AddButton(info, level)
-
- info.text = L["Use Focus Target"]
- info.func = toggleFocus
- info.checked = db.UseFocus
- info.tooltipTitle = L["Use Focus Target"]
- info.tooltipText = L["Tells Omen to additionally check your 'focus' and 'focustarget' before your 'target' and 'targettarget' in that order for threat display."]
- UIDropDownMenu_AddButton(info, level)
-
- info.text = L["Test Mode"]
- info.func = toggleTestMode
- info.checked = testMode
- info.tooltipTitle = L["Test Mode"]
- info.tooltipText = L["Tells Omen to enter Test Mode so that you can configure Omen's display much more easily."]
- UIDropDownMenu_AddButton(info, level)
-
- info.text = L["Open Config"]
- info.func = showConfig
- info.checked = nil
- info.tooltipTitle = L["Open Config"]
- info.tooltipText = L["Open Omen's configuration panel"]
- UIDropDownMenu_AddButton(info, level)
-
- info.text = L["Hide Omen"]
- info.func = toggle
- info.tooltipTitle = L["Hide Omen"]
- info.tooltipText = nil
- UIDropDownMenu_AddButton(info, level)
-
- -- Close menu item
- info.text = CLOSE
- info.func = self.HideMenu
- info.checked = nil
- info.arg1 = nil
- info.notCheckable = 1
- info.tooltipTitle = CLOSE
- UIDropDownMenu_AddButton(info, level)
- end
- end
-end
-
-
------------------------------------------------------------------------------
--- Omen config stuff
-
-function Omen:SetupOptions()
- LibStub("AceConfigRegistry-3.0"):RegisterOptionsTable("Omen", self.GenerateOptions)
- LibStub("AceConfig-3.0"):RegisterOptionsTable("OmenSlashCommand", self.OptionsSlash, "omen")
-
- -- The ordering here matters, it determines the order in the Blizzard Interface Options
- local ACD3 = LibStub("AceConfigDialog-3.0")
- self.optionsFrames = {}
- self.optionsFrames.Omen = ACD3:AddToBlizOptions("Omen", self.versionstring, nil, "General")
- self.optionsFrames.ShowWhen = ACD3:AddToBlizOptions("Omen", L["Show When..."], self.versionstring, "ShowWhen")
- self.optionsFrames.ShowClasses = ACD3:AddToBlizOptions("Omen", L["Show Classes..."], self.versionstring, "ShowClasses")
- self.optionsFrames.TitleBar = ACD3:AddToBlizOptions("Omen", L["Title Bar Settings"], self.versionstring, "TitleBar")
- self.optionsFrames.Bars = ACD3:AddToBlizOptions("Omen", L["Bar Settings"], self.versionstring, "Bars")
- self.optionsFrames.Warnings = ACD3:AddToBlizOptions("Omen", L["Warning Settings"], self.versionstring, "Warnings")
- self:RegisterModuleOptions("OmenSlashCommand", self.OptionsSlash, L["Slash Command"])
- self:RegisterModuleOptions("Profiles", function() return LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db) end, L["Profiles"])
- self.optionsFrames.Help = ACD3:AddToBlizOptions("Omen", L["Help File"], self.versionstring, "Help")
-
- self.SetupOptions = nil
-end
-
-function Omen:RegisterModuleOptions(name, optionTbl, displayName)
- if moduleOptions then
- moduleOptions[name] = optionTbl
- else
- self.Options.args[name] = (type(optionTbl) == "function") and optionTbl() or optionTbl
- end
- self.optionsFrames[name] = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("Omen", displayName, self.versionstring, name)
-end
-
-function Omen:ShowConfig()
- -- Open the profiles tab before, so the menu expands
- InterfaceOptionsFrame_OpenToCategory(self.optionsFrames.Profiles)
- InterfaceOptionsFrame_OpenToCategory(self.optionsFrames.Omen)
-end
-
-function Omen.GenerateOptions()
- if Omen.noconfig then assert(false, Omen.noconfig) end
- if not Omen.Options then
- Omen.GenerateOptionsInternal()
- Omen.GenerateOptionsInternal = nil
- moduleOptions = nil
- end
- return Omen.Options
-end
-
-
------------------------------------------------------------------------------
--- Omen config tables
-
--- Option table for the slash command only
-Omen.OptionsSlash = {
- type = "group",
- name = L["Slash Command"],
- order = -3,
- args = {
- intro = {
- order = 1,
- type = "description",
- name = L["OMEN_SLASH_DESC"],
- cmdHidden = true,
- },
- toggle = {
- type = "execute",
- name = L["Toggle Omen"],
- desc = L["Toggle Omen"].." ( /omen toggle )",
- func = function() Omen:Toggle() end,
- },
- center = {
- type = "execute",
- name = L["Center Omen"],
- desc = L["Center Omen"].." ( /omen center )",
- func = function()
- Omen.Anchor:ClearAllPoints()
- Omen.Anchor:SetPoint("CENTER", UIParent, "CENTER")
- Omen:SetAnchors()
- end,
- },
- config = {
- type = "execute",
- name = L["Configure"],
- desc = L["Open the configuration dialog"].." ( /omen config )",
- func = function() Omen:ShowConfig() end,
- guiHidden = true,
- },
- show = {
- type = "execute",
- name = L["Show Omen"],
- desc = L["Show Omen"].." ( /omen show )",
- func = function() Omen:Toggle(true) end,
- },
- hide = {
- type = "execute",
- name = L["Hide Omen"],
- desc = L["Hide Omen"].." ( /omen hide )",
- func = function() Omen:Toggle(false) end,
- },
- },
-}
-
--- This is to provide better error reporting feedback, and stop loading the rest of the file.
-if not AceGUIWidgetLSMlists then
- Omen.noconfig = 'Cannot find a library instance of "AceGUI-3.0-SharedMediaWidgets". Omen configuration will not be available.'
- assert(AceGUIWidgetLSMlists, Omen.noconfig)
-end
-
-function Omen.GenerateOptionsInternal()
- local outlines = {
- [""] = L["None"],
- ["OUTLINE"] = L["Outline"],
- ["THICKOUTLINE"] = L["Thick Outline"],
- }
-
- local function GetFuBarMinimapAttachedStatus(info)
- return Omen:IsFuBarMinimapAttached() or db.FuBar.HideMinimapButton
- end
-
--- Option table for the AceGUI config only
-Omen.Options = {
- type = "group",
- name = "Omen",
- get = function(info) return db[ info[#info] ] end,
- set = function(info, value) db[ info[#info] ] = value end,
- args = {
- General = {
- order = 1,
- type = "group",
- name = L["General Settings"],
- desc = L["General Settings"],
- args = {
- intro = {
- order = 1,
- type = "description",
- name = L["OMEN_DESC"],
- },
- Alpha = {
- order = 3,
- name = L["Alpha"],
- desc = L["Controls the transparency of the main Omen window."],
- type = "range",
- min = 0, max = 1, step = 0.01,
- isPercent = true,
- set = function(info, value)
- db.Alpha = value
- Omen.Anchor:SetAlpha(value)
- end,
- },
- Scale = {
- order = 4,
- name = L["Scale"],
- desc = L["Controls the scaling of the main Omen window."],
- type = "range",
- min = 0.50, max = 1.50, step = 0.01,
- isPercent = true,
- set = function(info, value)
- db.Scale = value
- Omen:SetAnchors()
- end,
- },
- FrameStrata = {
- type = "select",
- order = 5,
- name = L["Frame Strata"],
- desc = L["Controls the frame strata of the main Omen window. Default: MEDIUM"],
- values = { -- A hack to sort them in the menu
- ["1-BACKGROUND"] = "BACKGROUND",
- ["2-LOW"] = "LOW",
- ["3-MEDIUM"] = "MEDIUM",
- ["4-HIGH"] = "HIGH",
- ["5-DIALOG"] = "DIALOG",
- ["6-FULLSCREEN"] = "FULLSCREEN",
- ["7-FULLSCREEN_DIALOG"] = "FULLSCREEN_DIALOG",
- ["8-TOOLTIP"] = "TOOLTIP",
- },
- set = function(info, value)
- db.FrameStrata = value
- Omen.Anchor:SetFrameStrata(strsub(value, 3))
- end,
- },
- ClampToScreen = {
- type = "toggle",
- name = L["Clamp To Screen"],
- desc = L["Controls whether the main Omen window can be dragged offscreen"],
- order = 6,
- set = function(info, value)
- db.ClampToScreen = value
- Omen.Anchor:SetClampedToScreen(value)
- end,
- },
- Locked = {
- type = "toggle",
- name = L["Lock Omen"],
- desc = L["Locks Omen in place and prevents it from being dragged or resized."],
- order = 7,
- set = function(info, value)
- db.Locked = value
- Omen:UpdateGrips()
- end,
- },
- UseFocus = {
- type = "toggle",
- name = L["Use Focus Target"],
- desc = L["Tells Omen to additionally check your 'focus' and 'focustarget' before your 'target' and 'targettarget' in that order for threat display."],
- order = 8,
- set = function(info, value)
- Omen:ToggleFocus()
- end,
- },
- TestMode = {
- type = "toggle",
- name = L["Test Mode"],
- desc = L["Tells Omen to enter Test Mode so that you can configure Omen's display much more easily."],
- order = 9,
- get = function(info) return testMode end,
- set = function(info, value)
- testMode = value
- Omen:UpdateBars()
- end,
- },
- MinimapIcon = {
- type = "toggle",
- name = L["Show minimap button"],
- desc = L["Show the Omen minimap button"],
- order = 10,
- get = function(info) return not db.MinimapIcon.hide end,
- set = function(info, value)
- db.MinimapIcon.hide = not value
- if value then LDBIcon:Show("Omen") else LDBIcon:Hide("Omen") end
- end,
- hidden = function() return not LDBIcon or IsAddOnLoaded("Broker2FuBar") or IsAddOnLoaded("FuBar") end,
- },
- IgnorePlayerPets = {
- type = "toggle",
- name = L["Ignore Player Pets"],
- desc = L["IGNORE_PLAYER_PETS_DESC"],
- order = 11,
- set = function(info, value)
- db.IgnorePlayerPets = value
- Omen:UpdateBars()
- end,
- },
- ClickThrough = {
- type = "toggle",
- name = L["Click Through"],
- desc = L["Makes the Omen window non-interactive"],
- order = 12,
- set = function(info, value)
- db.ClickThrough = value
- Omen:UpdateClickThrough()
- end,
- },
- AutocollapseGroup = {
- type = "group",
- name = L["Autocollapse Options"],
- guiInline = true,
- order = 21,
- disabled = function() return not db.Autocollapse end,
- set = function(info, value)
- db[ info[#info] ] = value
- Omen:UpdateVisible()
- Omen:UpdateBars()
- end,
- args = {
- Autocollapse = {
- type = "toggle",
- name = L["Autocollapse"],
- desc = L["Collapse to show a minimum number of bars"],
- order = 1,
- set = function(info, value)
- db.Autocollapse = value
- Omen.Anchor:SetHeight(6*db.Bar.Height + 5*db.Bar.Spacing + Omen.Title:GetHeight() + 2*db.Background.BarInset)
- Omen:SetAnchors()
- Omen.BarList:Show()
- Omen:UpdateVisible()
- Omen:UpdateBars()
- end,
- disabled = false,
- },
- GrowUp = {
- order = 2,
- type = "toggle",
- name = L["Grow bars upwards"],
- desc = L["Grow bars upwards"],
- set = function(info, value)
- db.GrowUp = value
- Omen:SetAnchors()
- end,
- },
- CollapseHide = {
- order = 3,
- type = "toggle",
- name = L["Hide Omen on 0 bars"],
- desc = L["Hide Omen entirely if it collapses to show 0 bars"],
- set = function(info, value)
- db.CollapseHide = value
- if value then manualToggle = false end
- Omen:UpdateVisible()
- Omen:UpdateBars()
- end,
- },
- NumBars = {
- order = 4,
- name = L["Max bars to show"],
- desc = L["Max number of bars to show"],
- type = "range",
- min = 1, max = 40, step = 1,
- },
- },
- },
- Background = {
- type = "group",
- name = L["Background Options"],
- guiInline = true,
- order = 31,
- get = function(info) return db.Background[ info[#info] ] end,
- set = function(info, value)
- db.Background[ info[#info] ] = value
- Omen:UpdateBackdrop()
- end,
- args = {
- Texture = {
- type = "select", dialogControl = 'LSM30_Background',
- order = 1,
- name = L["Background Texture"],
- desc = L["Texture to use for the frame's background"],
- values = AceGUIWidgetLSMlists.background,
- },
- BorderTexture = {
- type = "select", dialogControl = 'LSM30_Border',
- order = 2,
- name = L["Border Texture"],
- desc = L["Texture to use for the frame's border"],
- values = AceGUIWidgetLSMlists.border,
- },
- Color = {
- type = "color",
- order = 3,
- name = L["Background Color"],
- desc = L["Frame's background color"],
- hasAlpha = true,
- get = function(info)
- local t = db.Background.Color
- return t.r, t.g, t.b, t.a
- end,
- set = function(info, r, g, b, a)
- local t = db.Background.Color
- t.r, t.g, t.b, t.a = r, g, b, a
- Omen:UpdateBackdrop()
- end,
- },
- BorderColor = {
- type = "color",
- order = 4,
- name = L["Border Color"],
- desc = L["Frame's border color"],
- hasAlpha = true,
- get = function(info)
- local t = db.Background.BorderColor
- return t.r, t.g, t.b, t.a
- end,
- set = function(info, r, g, b, a)
- local t = db.Background.BorderColor
- t.r, t.g, t.b, t.a = r, g, b, a
- Omen:UpdateBackdrop()
- end,
- },
- Tile = {
- type = "toggle",
- order = 5,
- name = L["Tile Background"],
- desc = L["Tile the background texture"],
- },
- TileSize = {
- type = "range",
- order = 6,
- name = L["Background Tile Size"],
- desc = L["The size used to tile the background texture"],
- min = 16, max = 256, step = 1,
- disabled = function() return not db.Background.Tile end,
- },
- EdgeSize = {
- type = "range",
- order = 7,
- name = L["Border Thickness"],
- desc = L["The thickness of the border"],
- min = 1, max = 16, step = 1,
- },
- BarInset = {
- type = "range",
- order = 8,
- name = L["Bar Inset"],
- desc = L["Sets how far inside the frame the threat bars will display from the 4 borders of the frame"],
- min = 1, max = 16, step = 1,
- },
- },
- },
- },
- },
- ShowWhen = {
- order = 2,
- type = "group",
- name = L["Show When..."],
- desc = L["Show Omen when..."],
- get = function(info) return db.ShowWith[ info[#info] ] end,
- set = function(info, value)
- db.ShowWith[ info[#info] ] = value
- manualToggle = false
- Omen:UpdateVisible()
- Omen:UpdateBars()
- end,
- args = {
- intro = {
- order = 1,
- type = "description",
- name = L["This section controls when Omen is automatically shown or hidden."],
- disabled = false,
- },
- UseShowWith = {
- type = "toggle",
- order = 2,
- name = L["Use Auto Show/Hide"],
- desc = L["Use Auto Show/Hide"],
- disabled = false,
- },
- ShowWithGroup = {
- type = "group",
- order = 3,
- guiInline = true,
- name = L["Use Auto Show/Hide"],
- desc = L["Use Auto Show/Hide"],
- disabled = function(info) return not db.ShowWith.UseShowWith end,
- args = {
- intro2 = {
- order = 10,
- type = "description",
- name = L["Show Omen when any of the following are true"],
- },
- Alone = {
- type = "toggle",
- order = 11,
- name = L["You are alone"],
- desc = L["Show Omen when you are alone"],
- },
- Party = {
- type = "toggle",
- order = 12,
- name = L["You are in a party"],
- desc = L["Show Omen when you are in a 5-man party"],
- },
- Raid = {
- type = "toggle",
- order = 13,
- name = L["You are in a raid"],
- desc = L["Show Omen when you are in a raid"],
- },
- Pet = {
- type = "toggle",
- order = 14,
- name = L["You have a pet"],
- desc = L["Show Omen when you have a pet out"],
- },
- intro3 = {
- order = 20,
- type = "description",
- name = L["However, hide Omen if any of the following are true (higher priority than the above)."],
- },
- HideInPVP = {
- type = "toggle",
- order = 21,
- width = "double",
- name = L["You are in a battleground"],
- desc = L["Turning this on will cause Omen to hide whenever you are in a battleground or arena."],
- },
- HideWhileResting = {
- type = "toggle",
- order = 22,
- width = "double",
- name = L["You are resting"],
- desc = L["Turning this on will cause Omen to hide whenever you are in a city or inn."],
- },
- HideWhenOOC = {
- type = "toggle",
- order = 23,
- width = "double",
- name = L["You are not in combat"],
- desc = L["Turning this on will cause Omen to hide whenever you are not in combat."],
- set = function(info, value)
- db.ShowWith.HideWhenOOC = value
- manualToggle = false
- if value then
- Omen:RegisterEvent("PLAYER_REGEN_DISABLED", "UpdateVisible")
- Omen:RegisterEvent("PLAYER_REGEN_ENABLED", "UpdateVisible")
- else
- Omen:UnregisterEvent("PLAYER_REGEN_DISABLED")
- Omen:UnregisterEvent("PLAYER_REGEN_ENABLED")
- end
- Omen:UpdateVisible()
- Omen:UpdateBars()
- end,
- },
- intro4 = {
- order = 30,
- type = "description",
- name = L["AUTO_SHOW/HIDE_NOTE"],
- },
- },
- },
- },
- },
- ShowClasses = {
- order = 3,
- type = "group",
- name = L["Show Classes..."],
- desc = L["Show Classes..."],
- args = {
- intro = {
- order = 1,
- type = "description",
- name = L["SHOW_CLASSES_DESC"],
- },
- Classes = {
- type = "multiselect",
- order = 30,
- name = L["Show bars for these classes"],
- values = showClassesOptionTable,
- get = function(info, k) return db.Bar.Classes[k] end,
- set = function(info, k, v)
- db.Bar.Classes[k] = v
- Omen:UpdateBars()
- end,
- },
- },
- },
- TitleBar = {
- order = 4,
- type = "group",
- name = L["Title Bar Settings"],
- desc = L["Title Bar Settings"],
- get = function(info) return db.TitleBar[ info[#info] ] end,
- set = function(info, value)
- db.TitleBar[ info[#info] ] = value
- Omen:UpdateTitleBar()
- Omen:UpdateBars()
- end,
- args = {
- intro = {
- order = 1,
- type = "description",
- name = L["Configure title bar settings."],
- },
- ShowTitleBar = {
- type = "toggle",
- order = 2,
- name = L["Show Title Bar"],
- desc = L["Show the Omen Title Bar"],
- },
- Height = {
- type = "range",
- order = 5,
- name = L["Title Bar Height"],
- desc = L["Height of the title bar. The minimum height allowed is twice the background border thickness."],
- min = 2, max = 32, step = 1,
- disabled = function() return not db.TitleBar.ShowTitleBar end,
- },
- TitleText = {
- type = "group",
- name = L["Title Text Options"],
- guiInline = true,
- order = 20,
- set = function(info, value)
- db.TitleBar[ info[#info] ] = value
- Omen:UpdateTitleBar()
- end,
- disabled = function() return not db.TitleBar.ShowTitleBar end,
- args = {
- Font = {
- type = "select", dialogControl = 'LSM30_Font',
- order = 1,
- name = L["Font"],
- desc = L["The font that the title text will use"],
- values = AceGUIWidgetLSMlists.font,
- },
- FontOutline = {
- type = "select",
- order = 2,
- name = L["Font Outline"],
- desc = L["The outline that the title text will use"],
- values = outlines,
- },
- FontColor = {
- type = "color",
- order = 3,
- name = L["Font Color"],
- desc = L["The color of the title text"],
- hasAlpha = true,
- get = function(info)
- local t = db.TitleBar.FontColor
- return t.r, t.g, t.b, t.a
- end,
- set = function(info, r, g, b, a)
- local t = db.TitleBar.FontColor
- t.r, t.g, t.b, t.a = r, g, b, a
- Omen:UpdateTitleBar()
- end,
- },
- FontSize = {
- type = "range",
- order = 4,
- name = L["Font Size"],
- desc = L["Control the font size of the title text"],
- min = 4, max = 30, step = 1,
- },
- },
- },
- UseSameBG = {
- type = "toggle",
- order = 30,
- width = "double",
- name = L["Use Same Background"],
- desc = L["Use the same background settings for the title bar as the main window's background"],
- set = function(info, value)
- db.TitleBar.UseSameBG = value
- Omen:UpdateBackdrop()
- end,
- disabled = function() return not db.TitleBar.ShowTitleBar end,
- },
- Background = {
- type = "group",
- name = L["Title Bar Background Options"],
- guiInline = true,
- order = 31,
- get = function(info) return db.TitleBar[ info[#info] ] end,
- set = function(info, value)
- db.TitleBar[ info[#info] ] = value
- Omen:UpdateBackdrop()
- end,
- disabled = function() return not db.TitleBar.ShowTitleBar or db.TitleBar.UseSameBG end,
- args = {
- Texture = {
- type = "select", dialogControl = 'LSM30_Background',
- order = 1,
- name = L["Background Texture"],
- desc = L["Texture to use for the frame's background"],
- values = AceGUIWidgetLSMlists.background,
- },
- BorderTexture = {
- type = "select", dialogControl = 'LSM30_Border',
- order = 2,
- name = L["Border Texture"],
- desc = L["Texture to use for the frame's border"],
- values = AceGUIWidgetLSMlists.border,
- },
- Color = {
- type = "color",
- order = 3,
- name = L["Background Color"],
- desc = L["Frame's background color"],
- hasAlpha = true,
- get = function(info)
- local t = db.TitleBar.Color
- return t.r, t.g, t.b, t.a
- end,
- set = function(info, r, g, b, a)
- local t = db.TitleBar.Color
- t.r, t.g, t.b, t.a = r, g, b, a
- Omen:UpdateBackdrop()
- end,
- },
- BorderColor = {
- type = "color",
- order = 4,
- name = L["Border Color"],
- desc = L["Frame's border color"],
- hasAlpha = true,
- get = function(info)
- local t = db.TitleBar.BorderColor
- return t.r, t.g, t.b, t.a
- end,
- set = function(info, r, g, b, a)
- local t = db.TitleBar.BorderColor
- t.r, t.g, t.b, t.a = r, g, b, a
- Omen:UpdateBackdrop()
- end,
- },
- Tile = {
- type = "toggle",
- order = 5,
- name = L["Tile Background"],
- desc = L["Tile the background texture"],
- },
- TileSize = {
- type = "range",
- order = 6,
- name = L["Background Tile Size"],
- desc = L["The size used to tile the background texture"],
- min = 16, max = 256, step = 1,
- disabled = function() return not db.TitleBar.ShowTitleBar or db.TitleBar.UseSameBG or not db.TitleBar.Tile end,
- },
- EdgeSize = {
- type = "range",
- order = 7,
- name = L["Border Thickness"],
- desc = L["The thickness of the border"],
- min = 1, max = 16, step = 1,
- },
- },
- },
- },
- },
- Bars = {
- order = 5,
- type = "group",
- name = L["Bar Settings"],
- desc = L["Bar Settings"],
- get = function(info) return db.Bar[ info[#info] ] end,
- set = function(info, value)
- db.Bar[ info[#info] ] = value
- Omen:UpdateBars()
- end,
- args = {
- intro = {
- order = 1,
- type = "description",
- name = L["Configure bar settings."],
- },
- AnimateBars = {
- type = "toggle",
- order = 2,
- name = L["Animate Bars"],
- desc = L["Smoothly animate bar changes"],
- },
- ShortNumbers = {
- type = "toggle",
- order = 3,
- name = L["Short Numbers"],
- desc = L["Display large numbers in Ks"],
- disabled = function() return not db.Bar.ShowValue end
- },
- Height = {
- type = "range",
- order = 4,
- name = L["Bar Height"],
- desc = L["Height of each bar"],
- min = 5, max = 50, step = 1, bigStep = 1,
- set = function(info, value)
- db.Bar.Height = value
- Omen:ReAnchorBars()
- Omen:ResizeBars()
- Omen:UpdateBars()
- end,
- },
- Spacing = {
- type = "range",
- order = 5,
- name = L["Bar Spacing"],
- desc = L["Spacing between each bar"],
- min = 0, max = 20, step = 1, bigStep = 1,
- set = function(info, value)
- db.Bar.Spacing = value
- Omen:ReAnchorBars()
- Omen:UpdateBars()
- end,
- },
- ShowTPS = {
- type = "toggle",
- order = 6,
- name = L["Show TPS"],
- desc = L["Show threat per second values"],
- set = function(info, value)
- db.Bar.ShowTPS = value
- if db.VGrip1 > db.VGrip2 then
- db.VGrip1, db.VGrip2 = db.VGrip2, db.VGrip1
- end
- movegrip1()
- movegrip2()
- Omen:UpdateGrips()
- local f = Omen.TPSUpdateFrame
- if f then
- if value then f:Show() else f:Hide() end
- end
- end,
- },
- TPSWindow = {
- type = "range",
- order = 7,
- name = L["TPS Window"],
- desc = L["TPS_WINDOW_DESC"],
- min = 3, max = 15, step = 0.1,
- disabled = function() return not db.Bar.ShowTPS end,
- },
- ShowValue = {
- type = "toggle",
- order = 8,
- name = L["Show Threat Values"],
- desc = L["Show Threat Values"],
- set = function(info, value)
- db.Bar.ShowValue = value
- if not value then
- db.Bar.ShowPercent = true
- bars[0].Text2:SetText(L["Threat"])
- elseif db.Bar.ShowPercent then
- bars[0].Text2:SetText(L["Threat [%]"])
- end
- Omen:UpdateBars()
- end,
- },
- ShowPercent = {
- type = "toggle",
- order = 9,
- name = L["Show Threat %"],
- desc = L["Show Threat %"],
- set = function(info, value)
- db.Bar.ShowPercent = value
- if not value then
- db.Bar.ShowValue = true
- bars[0].Text2:SetText(L["Threat"])
- elseif db.Bar.ShowValue then
- bars[0].Text2:SetText(L["Threat [%]"])
- end
- Omen:UpdateBars()
- end,
- },
- ShowHeadings = {
- type = "toggle",
- order = 11,
- name = L["Show Headings"],
- desc = L["Show column headings"],
- set = function(info, value)
- db.Bar.ShowHeadings = value
- Omen:ReAnchorBars()
- Omen:UpdateBars()
- end,
- },
- HeadingBGColor = {
- type = "color",
- order = 12,
- name = L["Heading BG Color"],
- desc = L["Heading background color"],
- hasAlpha = true,
- get = function(info)
- local t = db.Bar.HeadingBGColor
- return t.r, t.g, t.b, t.a
- end,
- set = function(info, r, g, b, a)
- local t = db.Bar.HeadingBGColor
- t.r, t.g, t.b, t.a = r, g, b, a
- Omen:UpdateBarLabelSettings()
- Omen:UpdateBars()
- end,
- disabled = function() return not db.Bar.ShowHeadings end,
- },
- UseMyBarColor = {
- type = "toggle",
- order = 13,
- name = L["Use 'My Bar' color"],
- desc = L["Use a different colored background for your threat bar in Omen"],
- },
- MyBarColor = {
- type = "color",
- order = 14,
- name = L["'My Bar' BG Color"],
- desc = L["The background color for your threat bar"],
- hasAlpha = true,
- get = function(info)
- local t = db.Bar.MyBarColor
- return t.r, t.g, t.b, t.a
- end,
- set = function(info, r, g, b, a)
- local t = db.Bar.MyBarColor
- t.r, t.g, t.b, t.a = r, g, b, a
- Omen:UpdateBars()
- end,
- disabled = function() return not db.Bar.UseMyBarColor end,
- },
- UseTankBarColor = {
- type = "toggle",
- order = 15,
- name = L["Use Tank Bar color"],
- desc = L["Use a different colored background for the tank's threat bar in Omen"],
- },
- TankBarColor = {
- type = "color",
- order = 16,
- name = L["Tank Bar Color"],
- desc = L["The background color for your tank's threat bar"],
- hasAlpha = true,
- get = function(info)
- local t = db.Bar.TankBarColor
- return t.r, t.g, t.b, t.a
- end,
- set = function(info, r, g, b, a)
- local t = db.Bar.TankBarColor
- t.r, t.g, t.b, t.a = r, g, b, a
- Omen:UpdateBars()
- end,
- disabled = function() return not db.Bar.UseTankBarColor end,
- },
- ShowAggroBar = {
- type = "toggle",
- order = 17,
- name = L["Show Pull Aggro Bar"],
- desc = L["Show a bar for the amount of threat you will need to reach in order to pull aggro."],
- },
- AggroBarColor = {
- type = "color",
- order = 18,
- name = L["Pull Aggro Bar Color"],
- desc = L["The background color for your Pull Aggro bar"],
- hasAlpha = true,
- get = function(info)
- local t = db.Bar.AggroBarColor
- return t.r, t.g, t.b, t.a
- end,
- set = function(info, r, g, b, a)
- local t = db.Bar.AggroBarColor
- t.r, t.g, t.b, t.a = r, g, b, a
- Omen:UpdateBars()
- end,
- disabled = function() return not db.Bar.ShowAggroBar end,
- },
- UseClassColors = {
- type = "toggle",
- order = 21,
- name = L["Use Class Colors"],
- desc = L["Use standard class colors for the background color of threat bars"],
- },
- PetBarColor = {
- type = "color",
- order = 22,
- name = L["Pet Bar Color"],
- desc = L["The background color for pets"],
- hasAlpha = true,
- get = function(info)
- local t = db.Bar.PetBarColor
- return t.r, t.g, t.b, t.a
- end,
- set = function(info, r, g, b, a)
- local t = db.Bar.PetBarColor
- t.r, t.g, t.b, t.a = r, g, b, a
- Omen:UpdateBars()
- end,
- disabled = function() return not db.Bar.UseClassColors end,
- },
- UseCustomClassColors = {
- type = "toggle",
- order = 23,
- name = L["Use !ClassColors"],
- desc = L["Use !ClassColors addon for class colors for the background color of threat bars"],
- set = function(info, v)
- db.Bar.UseCustomClassColors = v
- Omen:UpdateRaidClassColors()
- end,
- disabled = function() return not db.Bar.UseClassColors or not CUSTOM_CLASS_COLORS end,
- },
- FadeBarColor = {
- type = "color",
- order = 24,
- name = L["Fade/MI Bar Color"],
- desc = L["The background color for players under the effects of Fade and Mirror Image (they will be at negative 4 million threat)"],
- hasAlpha = true,
- get = function(info)
- local t = db.Bar.FadeBarColor
- return t.r, t.g, t.b, t.a
- end,
- set = function(info, r, g, b, a)
- local t = db.Bar.FadeBarColor
- t.r, t.g, t.b, t.a = r, g, b, a
- Omen:UpdateBars()
- end,
- },
- BarColor = {
- type = "color",
- order = 25,
- name = L["Bar BG Color"],
- desc = L["The background color for all threat bars"],
- hasAlpha = true,
- get = function(info)
- local t = db.Bar.BarColor
- return t.r, t.g, t.b, t.a
- end,
- set = function(info, r, g, b, a)
- local t = db.Bar.BarColor
- t.r, t.g, t.b, t.a = r, g, b, a
- Omen:UpdateBars()
- end,
- disabled = function() return db.Bar.UseClassColors end,
- },
- AlwaysShowSelf = {
- type = "toggle",
- order = 26,
- name = L["Always Show Self"],
- desc = L["Always show your threat bar on Omen (ignores class filter settings), showing your bar on the last row if necessary"],
- },
- InvertColors = {
- type = "toggle",
- order = 27,
- name = L["Invert Bar/Text Colors"],
- desc = L["Switch the colors so that the bar background colors and the text colors are swapped."],
- set = function(info, v)
- db.Bar.InvertColors = v
- Omen:UpdateBarLabelSettings()
- Omen:UpdateBars()
- end,
- },
- Texture = {
- type = "select", dialogControl = 'LSM30_Statusbar',
- order = 29,
- name = L["Bar Texture"],
- desc = L["The texture that the bar will use"],
- values = AceGUIWidgetLSMlists.statusbar,
- set = function(info, v)
- db.Bar.Texture = v
- Omen:UpdateBarTextureSettings()
- end,
- },
- BarLabelsGroup = {
- type = "group",
- name = L["Bar Label Options"],
- guiInline = true,
- order = 30,
- set = function(info, v)
- db.Bar[ info[#info] ] = v
- Omen:UpdateBarLabelSettings()
- Omen:UpdateBars()
- end,
- args = {
- Font = {
- type = "select", dialogControl = 'LSM30_Font',
- order = 1,
- name = L["Font"],
- desc = L["The font that the labels will use"],
- values = AceGUIWidgetLSMlists.font,
- },
- FontOutline = {
- type = "select",
- order = 2,
- name = L["Font Outline"],
- desc = L["The outline that the labels will use"],
- values = outlines,
- },
- FontColor = {
- type = "color",
- order = 3,
- name = L["Font Color"],
- desc = L["The color of the labels"],
- hasAlpha = true,
- get = function(info)
- local t = db.Bar.FontColor
- return t.r, t.g, t.b, t.a
- end,
- set = function(info, r, g, b, a)
- local t = db.Bar.FontColor
- t.r, t.g, t.b, t.a = r, g, b, a
- Omen:UpdateBarLabelSettings()
- Omen:UpdateBars()
- end,
- },
- FontSize = {
- type = "range",
- order = 4,
- name = L["Font Size"],
- desc = L["Control the font size of the labels"],
- min = 4, max = 30, step = 1,
- },
- },
- },
- },
- },
- Warnings = {
- order = 6,
- type = "group",
- name = L["Warning Settings"],
- desc = L["Warning Settings"],
- get = function(info) return db.Warnings[ info[#info] ] end,
- set = function(info, value)
- db.Warnings[ info[#info] ] = value
- end,
- args = {
- intro = {
- order = 1,
- type = "description",
- name = L["OMEN_WARNINGS_DESC"],
- },
- Sound = {
- type = "toggle",
- order = 2,
- name = L["Enable Sound"],
- desc = L["Causes Omen to play a chosen sound effect"],
- },
- Flash = {
- type = "toggle",
- order = 3,
- name = L["Enable Screen Flash"],
- desc = L["Causes the entire screen to flash red momentarily"],
- },
- Shake = {
- type = "toggle",
- order = 4,
- name = L["Enable Screen Shake"],
- desc = L["Causes the entire game world to shake momentarily. This option only works if nameplates are turned off."],
- },
- Message = {
- type = "toggle",
- order = 5,
- name = L["Enable Warning Message"],
- desc = L["Print a message to screen when you accumulate too much threat"],
- },
- Output = Omen:GetSinkAce3OptionsDataTable(),
- Threshold = {
- type = "range",
- order = 7,
- name = L["Warning Threshold %"],
- desc = L["Warning Threshold %"],
- min = 60, max = 130, step = 1,
- },
- SoundFile = {
- type = "select", dialogControl = 'LSM30_Sound',
- order = 8,
- name = L["Sound to play"],
- desc = L["Sound to play"],
- values = AceGUIWidgetLSMlists.sound,
- disabled = function() return not db.Warnings.Sound end,
- },
- DisableWhileTanking = {
- type = "toggle",
- order = 9,
- name = L["Disable while tanking"],
- desc = L["DISABLE_WHILE_TANKING_DESC"],
- },
- test = {
- type = "execute",
- order = -1,
- name = L["Test warnings"],
- desc = L["Test warnings"],
- func = function()
- local t = db.Warnings
- Omen:Warn(t.Sound, t.Flash, t.Shake, t.Message and L["Test warnings"])
- end,
- },
- },
- },
- FuBar = {
- order = -4,
- type = "group",
- name = L["FuBar Options"],
- desc = L["FuBar Options"],
- hidden = function() return Omen.IsFuBarMinimapAttached == nil end,
- args = {
- hideIcon = {
- type = "toggle",
- order = 1,
- name = L["Hide minimap/FuBar icon"],
- desc = L["Hide minimap/FuBar icon"],
- get = function(info) return db.FuBar.HideMinimapButton end,
- set = function(info, v)
- db.FuBar.HideMinimapButton = v
- Omen:UpdateFuBarSettings()
- end,
- },
- attachMinimap = {
- type = "toggle",
- order = 2,
- name = L["Attach to minimap"],
- desc = L["Attach to minimap"],
- get = function(info) return Omen:IsFuBarMinimapAttached() end,
- set = function(info, v)
- Omen:ToggleFuBarMinimapAttached()
- db.FuBar.AttachMinimap = Omen:IsFuBarMinimapAttached()
- end,
- disabled = function() return db.FuBar.HideMinimapButton end,
- },
- showIcon = {
- type = "toggle",
- order = 3,
- name = L["Show icon"],
- desc = L["Show icon"],
- get = function(info) return Omen:IsFuBarIconShown() end,
- set = function(info, v) Omen:ToggleFuBarIconShown() end,
- disabled = GetFuBarMinimapAttachedStatus,
- },
- showText = {
- type = "toggle",
- order = 4,
- name = L["Show text"],
- desc = L["Show text"],
- get = function(info) return Omen:IsFuBarTextShown() end,
- set = function(info, v) Omen:ToggleFuBarTextShown() end,
- disabled = GetFuBarMinimapAttachedStatus,
- },
- position = {
- type = "select",
- order = 5,
- name = L["Position"],
- desc = L["Position"],
- values = {LEFT = L["Left"], CENTER = L["Center"], RIGHT = L["Right"]},
- get = function() return Omen:GetPanel() and Omen:GetPanel():GetPluginSide(Omen) end,
- set = function(info, val)
- if Omen:GetPanel() and Omen:GetPanel().SetPluginSide then
- Omen:GetPanel():SetPluginSide(Omen, val)
- end
- end,
- disabled = GetFuBarMinimapAttachedStatus,
- }
- }
- },
- Help = {
- type = "group",
- order = -1,
- name = L["Help File"],
- desc = L["A collection of help pages"],
- childGroups = "select",
- args = {
- FAQ1 = {
- type = "group",
- order = 1,
- name = L["FAQ Part 1"],
- args = {
- header = {
- type = "header",
- name = L["Frequently Asked Questions"],
- order = 0,
- },
- text = {
- order = 1,
- type = "description",
- name = L["GENERAL_FAQ"],
- },
- },
- },
- FAQ2 = {
- type = "group",
- order = 2,
- name = L["FAQ Part 2"],
- args = {
- header = {
- type = "header",
- name = L["Frequently Asked Questions"],
- order = 0,
- },
- text = {
- order = 1,
- type = "description",
- name = L["GENERAL_FAQ2"],
- },
- },
- },
- WARRIOR = {
- type = "group",
- name = L["Warrior"],
- args = {
- header = {
- type = "header",
- name = L["Warrior"],
- order = 0,
- },
- text = {
- order = 1,
- type = "description",
- name = L["WARRIOR_FAQ"],
- },
- },
- },
- },
- },
- },
-}
-Omen.Options.args.Warnings.args.Output.order = 6
-Omen.Options.args.Warnings.args.Output.inline = true
-Omen.Options.args.Warnings.args.Output.disabled = function() return not db.Warnings.Message end
-
- for k, v in pairs(moduleOptions) do
- Omen.Options.args[k] = (type(v) == "function") and v() or v
- end
-
- -- Add ordering data to the option table generated by AceDBOptions-3.0
- Omen.Options.args.Profiles.order = -2
-
- local h = db.Background.EdgeSize * 2
- if not db.TitleBar.UseSameBG then h = db.TitleBar.EdgeSize * 2 end
- Omen.Options.args.TitleBar.args.Height.min = h
-end
-
+-----------------------------------------------------------------------------
+-- Addon declaration
+local Omen = LibStub("AceAddon-3.0"):NewAddon("Omen", "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0", "LibSink-2.0")
+local L = LibStub("AceLocale-3.0"):GetLocale("Omen", false)
+local LSM = LibStub("LibSharedMedia-3.0")
+local LDB = LibStub("LibDataBroker-1.1", true)
+local LDBIcon = LDB and LibStub("LibDBIcon-1.0", true)
+Omen.version = GetAddOnMetadata("Omen", "Version")
+Omen.versionstring = "Omen v"..GetAddOnMetadata("Omen", "Version")
+_G["Omen"] = Omen
+
+
+-----------------------------------------------------------------------------
+-- Keybinding globals
+BINDING_HEADER_OMEN = Omen.versionstring
+BINDING_NAME_OMENTOGGLE = L["Toggle Omen"]
+BINDING_NAME_OMENTOGGLEFOCUS = L["Toggle Focus"]
+
+
+-----------------------------------------------------------------------------
+-- Register some media
+LSM:Register("sound", "Rubber Ducky", [[Sound\Doodad\Goblin_Lottery_Open01.wav]])
+LSM:Register("sound", "Cartoon FX", [[Sound\Doodad\Goblin_Lottery_Open03.wav]])
+LSM:Register("sound", "Explosion", [[Sound\Doodad\Hellfire_Raid_FX_Explosion05.wav]])
+LSM:Register("sound", "Shing!", [[Sound\Doodad\PortcullisActive_Closed.wav]])
+LSM:Register("sound", "Wham!", [[Sound\Doodad\PVP_Lordaeron_Door_Open.wav]])
+LSM:Register("sound", "Simon Chime", [[Sound\Doodad\SimonGame_LargeBlueTree.wav]])
+LSM:Register("sound", "War Drums", [[Sound\Event Sounds\Event_wardrum_ogre.wav]])
+LSM:Register("sound", "Cheer", [[Sound\Event Sounds\OgreEventCheerUnique.wav]])
+LSM:Register("sound", "Humm", [[Sound\Spells\SimonGame_Visual_GameStart.wav]])
+LSM:Register("sound", "Short Circuit", [[Sound\Spells\SimonGame_Visual_BadPress.wav]])
+LSM:Register("sound", "Fel Portal", [[Sound\Spells\Sunwell_Fel_PortalStand.wav]])
+LSM:Register("sound", "Fel Nova", [[Sound\Spells\SeepingGaseous_Fel_Nova.wav]])
+LSM:Register("sound", "You Will Die!", [[Sound\Creature\CThun\CThunYouWillDIe.wav]])
+LSM:Register("sound", "Omen: Aoogah!", [[Interface\AddOns\Omen\aoogah.ogg]])
+
+
+-----------------------------------------------------------------------------
+-- Localize some global functions
+local floor, format, random, pairs, type = floor, format, random, pairs, type
+local tinsert, tremove, next, sort, wipe = tinsert, tremove, next, sort, wipe
+local RAID_CLASS_COLORS = RAID_CLASS_COLORS
+local UnitDetailedThreatSituation = UnitDetailedThreatSituation
+local UnitExists, UnitGUID, UnitName, UnitClass, UnitHealth = UnitExists, UnitGUID, UnitName, UnitClass, UnitHealth
+local UnitIsPlayer, UnitPlayerControlled, UnitCanAttack = UnitIsPlayer, UnitPlayerControlled, UnitCanAttack
+local GetNumRaidMembers, GetNumPartyMembers = GetNumRaidMembers, GetNumPartyMembers
+
+
+-----------------------------------------------------------------------------
+-- Local variables used
+local db
+local defaults = {
+ profile = {
+ Alpha = 1,
+ Scale = 1,
+ GrowUp = false,
+ Autocollapse = false,
+ NumBars = 10,
+ CollapseHide = false,
+ Locked = false,
+ PositionW = 200,
+ PositionH = 82,
+ VGrip1 = 85,
+ VGrip2 = 115,
+ UseFocus = false,
+ IgnorePlayerPets = true,
+ FrameStrata = "3-MEDIUM",
+ ClampToScreen = true,
+ ClickThrough = false,
+ Background = {
+ Texture = "Blizzard Parchment",
+ BorderTexture = "Blizzard Dialog",
+ Color = {r = 1, g = 1, b = 1, a = 1,},
+ BorderColor = {r = 0.8, g = 0.6, b = 0, a = 1,},
+ Tile = false,
+ TileSize = 32,
+ EdgeSize = 8,
+ BarInset = 3,
+ },
+ TitleBar = {
+ Height = 16,
+ Font = "Friz Quadrata TT",
+ FontOutline = "",
+ FontColor = {r = 1, g = 1, b = 1, a = 1,},
+ FontSize = 10,
+ ShowTitleBar = true,
+ UseSameBG = true,
+ Texture = "Blizzard Parchment",
+ BorderTexture = "Blizzard Dialog",
+ Color = {r = 1, g = 1, b = 1, a = 1,},
+ BorderColor = {r = 0.8, g = 0.6, b = 0, a = 1,},
+ Tile = false,
+ TileSize = 32,
+ EdgeSize = 8,
+ },
+ Bar = {
+ Texture = "Blizzard",
+ Height = 12,
+ Spacing = 0,
+ AnimateBars = true,
+ ShortNumbers = true,
+ Font = "Friz Quadrata TT",
+ FontOutline = "",
+ FontColor = {r = 1, g = 1, b = 1, a = 1,},
+ FontSize = 10,
+ Classes = {
+ DEATHKNIGHT = true,
+ DRUID = true,
+ HUNTER = true,
+ MAGE = true,
+ PALADIN = true,
+ PET = true,
+ PRIEST = true,
+ ROGUE = true,
+ SHAMAN = true,
+ WARLOCK = true,
+ WARRIOR = true,
+ HERO = true,
+ ["*NOTINPARTY*"] = true,
+ },
+ ShowTPS = true,
+ TPSWindow = 10,
+ ShowHeadings = true,
+ HeadingBGColor = {r = 0, g = 0, b = 0, a = 0,},
+ UseMyBarColor = false,
+ MyBarColor = {r = 1, g = 0, b = 0, a = 1,},
+ ShowPercent = true,
+ ShowValue = true,
+ UseClassColors = true,
+ BarColor = {r = 1, g = 0, b = 0, a = 1,},
+ UseTankBarColor = false,
+ TankBarColor = {r = 1, g = 0, b = 0, a = 1,},
+ AlwaysShowSelf = true,
+ ShowAggroBar = true,
+ AggroBarColor = {r = 1, g = 0, b = 0, a = 1,},
+ PetBarColor = {r = 0.77, g = 0, b = 1, a = 1},
+ FadeBarColor = {r = 0.5, g = 0.5, b = 0.5, a = 1},
+ UseCustomClassColors = true,
+ InvertColors = false,
+ },
+ ShowWith = {
+ UseShowWith = true,
+ Pet = true,
+ Alone = false,
+ Party = true,
+ Raid = true,
+ -- Deprecated SV values
+ -- Resting = false, PVP = false, Dungeon = true, ShowOnlyInCombat = false,
+ HideWhileResting = true,
+ HideInPVP = true,
+ HideWhenOOC = false,
+ },
+ FuBar = {
+ HideMinimapButton = true,
+ AttachMinimap = false,
+ },
+ Warnings = {
+ Sound = true,
+ Flash = true,
+ Shake = false,
+ Message = false,
+ SinkOptions = {},
+ Threshold = 90,
+ SoundFile = "Fel Nova",
+ DisableWhileTanking = true,
+ },
+ MinimapIcon = {
+ hide = false,
+ minimapPos = 220,
+ radius = 80,
+ },
+ },
+}
+local guidNameLookup = {} -- Format: guidNameLookup[guid] = "Unit Name"
+local guidClassLookup = {} -- Format: guidClassLookup[guid] = "CLASS"
+local timers = {} -- Format: timers.timerName = timer returned from AceTimer-3.0
+local bars = {} -- Format: bars[i] = frame containing the i-th bar from the top of Omen
+local inRaid, inParty -- boolean variables indicating if the player is in a raid and/or party
+local testMode = false -- boolean: Are we in test mode?
+local manualToggle = false -- boolean: Did we manually toggle Omen?
+local moduleOptions = {} -- Table for LoD module options registration
+
+Omen.GuidNameLookup = guidNameLookup
+Omen.GuidClassLookup = guidClassLookup
+Omen.Timers = timers
+Omen.Bars = bars
+setmetatable(guidNameLookup, {__index = function(self, guid) return L[""] end})
+
+-- For speedups. Rather than concantenating every time we need a unitID, we just look
+-- it up instead. That is rID[i] is much faster than format("raid%d", i) or "raid"..i
+local pID = {}
+local ptID = {}
+local ppID = {}
+local pptID = {}
+local rID = {}
+local rtID = {}
+local rpID = {}
+local rptID = {}
+for i = 1, 4 do
+ pID[i] = format("party%d", i)
+ ptID[i] = format("party%dtarget", i)
+ ppID[i] = format("partypet%d", i)
+ pptID[i] = format("partypet%dtarget", i)
+end
+for i = 1, 40 do
+ rID[i] = format("raid%d", i)
+ rtID[i] = format("raid%dtarget", i)
+ rpID[i] = format("raidpet%d", i)
+ rptID[i] = format("raidpet%dtarget", i)
+end
+local showClassesOptionTable = {
+ DEATHKNIGHT = L["DEATHKNIGHT"],
+ DRUID = L["DRUID"],
+ HUNTER = L["HUNTER"],
+ MAGE = L["MAGE"],
+ PALADIN = L["PALADIN"],
+ PET = L["PET"],
+ PRIEST = L["PRIEST"],
+ ROGUE = L["ROGUE"],
+ SHAMAN = L["SHAMAN"],
+ WARLOCK = L["WARLOCK"],
+ WARRIOR = L["WARRIOR"],
+ HERO = L["HERO"],
+ ["*NOTINPARTY*"] = L["*Not in Party*"],
+}
+
+
+----------------------------------------------------------------------------------------
+-- Use a common frame and setup some common functions for the Omen dropdown menus
+local Omen_DropDownMenu = CreateFrame("Frame", "Omen_DropDownMenu")
+Omen_DropDownMenu.displayMode = "MENU"
+Omen_DropDownMenu.info = {}
+Omen_DropDownMenu.HideMenu = function()
+ if UIDROPDOWNMENU_OPEN_MENU == Omen_DropDownMenu then
+ CloseDropDownMenus()
+ end
+end
+Omen_DropDownMenu.OnClick = function(frame, button, down)
+ if Omen_DropDownMenu.initialize ~= frame.initMenuFunc then
+ CloseDropDownMenus()
+ Omen_DropDownMenu.initialize = frame.initMenuFunc
+ end
+ ToggleDropDownMenu(1, nil, Omen_DropDownMenu, frame, 0, 0)
+end
+
+
+-----------------------------------------------------------------------------
+-- Table Pool for recycling tables
+local tablePool = {}
+setmetatable(tablePool, {__mode = "kv"}) -- Weak table
+
+-- Get a new table
+local function newTable()
+ local t = next(tablePool) or {}
+ tablePool[t] = nil
+ return t
+end
+
+-- Delete table and return to pool -- Recursive!! -- Use with care!!
+local function delTable(t)
+ if type(t) == "table" then
+ for k, v in pairs(t) do
+ if type(v) == "table" then
+ delTable(v) -- child tables get put into the pool
+ end
+ t[k] = nil
+ end
+ t[true] = true -- resize table to 1 item
+ t[true] = nil
+ setmetatable(t, nil)
+ tablePool[t] = true
+ end
+ return nil -- return nil to assign input reference
+end
+
+
+-----------------------------------------------------------------------------
+-- Omen initialization and frame functions
+
+local function startmoving(self)
+ if not db.Locked then
+ Omen.Anchor.IsMovingOrSizing = 1
+ Omen.Anchor:StartMoving()
+ end
+end
+local function stopmoving(self)
+ if Omen.Anchor.IsMovingOrSizing then
+ Omen.Anchor:StopMovingOrSizing()
+ Omen:SetAnchors()
+ Omen:UpdateBars()
+ Omen.Anchor.IsMovingOrSizing = nil
+ end
+ if self == Omen.Anchor then db.Shown = self:IsShown() end
+end
+local function sizing(self)
+ local w = Omen.Anchor:GetWidth()
+ db.VGrip1 = w * Omen.Anchor.VGrip1Ratio
+ db.VGrip2 = w * Omen.Anchor.VGrip2Ratio
+ if db.VGrip1 < 10 then db.VGrip1 = 10 end
+ if db.VGrip1 > w - 10 then db.VGrip1 = w - 10 end
+ if db.Bar.ShowTPS then
+ if db.VGrip2 < db.VGrip1 + 10 then db.VGrip2 = db.VGrip1 + 10 end
+ if db.VGrip1 > w - 20 then
+ db.VGrip1 = w - 20
+ db.VGrip2 = w - 10
+ end
+ Omen.VGrip2:ClearAllPoints()
+ Omen.VGrip2:SetPoint("TOPLEFT", Omen.BarList, "TOPLEFT", db.VGrip2, 0)
+ Omen.VGrip2:SetPoint("BOTTOMLEFT", Omen.BarList, "BOTTOMLEFT", db.VGrip2, 0)
+ end
+ Omen.VGrip1:ClearAllPoints()
+ Omen.VGrip1:SetPoint("TOPLEFT", Omen.BarList, "TOPLEFT", db.VGrip1, 0)
+ Omen.VGrip1:SetPoint("BOTTOMLEFT", Omen.BarList, "BOTTOMLEFT", db.VGrip1, 0)
+ Omen:ResizeBars()
+ Omen:ReAnchorLabels()
+ Omen:UpdateBars()
+end
+local function movegrip1(self)
+ local x = GetCursorPosition() / UIParent:GetEffectiveScale() / Omen.Anchor:GetScale()
+ local x1 = Omen.Anchor:GetLeft() + 10
+ local x2 = db.Bar.ShowTPS and Omen.Anchor:GetLeft() + db.VGrip2 - 10 or Omen.Anchor:GetRight() - 10
+ if x > x1 and x < x2 then
+ db.VGrip1 = x - x1 + 10
+ Omen.VGrip1:ClearAllPoints()
+ Omen.VGrip1:SetPoint("TOPLEFT", Omen.BarList, "TOPLEFT", db.VGrip1, 0)
+ Omen.VGrip1:SetPoint("BOTTOMLEFT", Omen.BarList, "BOTTOMLEFT", db.VGrip1, 0)
+ end
+ Omen:ReAnchorLabels()
+end
+local function movegrip2(self)
+ local x = GetCursorPosition() / UIParent:GetEffectiveScale() / Omen.Anchor:GetScale()
+ local x1 = Omen.Anchor:GetLeft() + db.VGrip1 + 10
+ local x2 = Omen.Anchor:GetRight() - 10
+ if x > x1 and x < x2 then
+ db.VGrip2 = x - x1 + db.VGrip1 + 10
+ Omen.VGrip2:ClearAllPoints()
+ Omen.VGrip2:SetPoint("TOPLEFT", Omen.BarList, "TOPLEFT", db.VGrip2, 0)
+ Omen.VGrip2:SetPoint("BOTTOMLEFT", Omen.BarList, "BOTTOMLEFT", db.VGrip2, 0)
+ end
+ Omen:ReAnchorLabels()
+end
+
+function Omen:CreateFrames()
+ -- Create anchor
+ self.Anchor = CreateFrame("Frame", "OmenAnchor", UIParent)
+ self.Anchor:SetResizable(true)
+ self.Anchor:SetMinResize(90, db.Background.EdgeSize * 2)
+ self.Anchor:SetMovable(true)
+ self.Anchor:SetPoint("CENTER", UIParent, "CENTER")
+ self.Anchor:SetWidth(225)
+ self.Anchor:SetHeight(150)
+ self.Anchor:SetScript("OnHide", stopmoving)
+ self.Anchor:SetScript("OnShow", function(self) db.Shown = true Omen:UpdateBars() end)
+
+ -- Create Title
+ self.Title = CreateFrame("Button", "OmenTitle", self.Anchor)
+ self.Title:SetPoint("TOPLEFT", self.Anchor, "TOPLEFT")
+ self.Title:SetPoint("TOPRIGHT", self.Anchor, "TOPRIGHT")
+ self.Title:SetHeight(16)
+ self.Title:SetMinResize(90, db.Background.EdgeSize * 2)
+ self.Title:EnableMouse(true)
+ self.Title:SetScript("OnMouseDown", startmoving)
+ self.Title:SetScript("OnMouseUp", stopmoving)
+ self.Title:SetScript("OnClick", Omen_DropDownMenu.OnClick)
+ self.Title.initMenuFunc = self.TitleQuickMenu
+ self.Title:RegisterForClicks("RightButtonUp")
+
+ -- Create Title text
+ self.TitleText = self.Title:CreateFontString(nil, nil, "GameFontNormal")
+ self.TitleText:SetPoint("LEFT", self.Title, "LEFT", 8, 1)
+ self.TitleText:SetJustifyH("LEFT")
+ self.TitleText:SetTextColor(1, 1, 1, 1)
+ self.defaultTitle = "Omen|cffffcc003|r"
+ self.TitleText:SetText(self.defaultTitle)
+
+ -- Create Bar List
+ self.BarList = CreateFrame("Frame", "OmenBarList", self.Anchor)
+ self.BarList:SetResizable(true)
+ self.BarList:EnableMouse(true)
+ self.BarList:SetPoint("TOPLEFT", self.Title, "BOTTOMLEFT")
+ self.BarList:SetPoint("BOTTOMRIGHT", self.Anchor, "BOTTOMRIGHT")
+ self.BarList:SetScript("OnMouseDown", startmoving)
+ self.BarList:SetScript("OnMouseUp", stopmoving)
+ self.BarList:SetScript("OnHide", stopmoving)
+ self.BarList.barsShown = 0
+
+ -- Create resizing corner grip
+ self.Grip = CreateFrame("Button", "OmenResizeGrip", self.BarList)
+ self.Grip:SetNormalTexture("Interface\\AddOns\\Omen\\ResizeGrip")
+ self.Grip:SetHighlightTexture("Interface\\AddOns\\Omen\\ResizeGrip")
+ self.Grip:SetWidth(16)
+ self.Grip:SetHeight(16)
+ self.Grip:SetPoint("BOTTOMRIGHT", self.BarList, "BOTTOMRIGHT", 0, 1)
+ self.Grip:SetScript("OnMouseDown", function(self, button)
+ if not db.Locked then
+ Omen.Anchor.IsMovingOrSizing = 2
+ Omen.Anchor.VGrip1Ratio = db.VGrip1 / Omen.Anchor:GetWidth()
+ Omen.Anchor.VGrip2Ratio = db.VGrip2 / Omen.Anchor:GetWidth()
+ Omen.Anchor:SetScript("OnSizeChanged", sizing)
+ Omen.Anchor:StartSizing()
+ end
+ end)
+ self.Grip:SetScript("OnMouseUp", function(self)
+ if Omen.Anchor.IsMovingOrSizing == 2 then
+ Omen.Anchor:SetScript("OnSizeChanged", nil)
+ Omen.Anchor:StopMovingOrSizing()
+ sizing()
+ Omen:SetAnchors()
+ Omen.Anchor.IsMovingOrSizing = nil
+ Omen.Anchor.VGrip1Ratio = nil
+ end
+ end)
+ self.Grip:SetScript("OnHide", self.Grip:GetScript("OnMouseUp"))
+
+ -- Create label resizing vertical grip 1
+ self.VGrip1 = CreateFrame("Button", "OmenVResizeGrip1", self.BarList)
+ self.VGrip1:SetWidth(1)
+ self.VGrip1:SetPoint("TOPLEFT", self.BarList, "TOPLEFT", db.VGrip1, 0)
+ self.VGrip1:SetPoint("BOTTOMLEFT", self.BarList, "BOTTOMLEFT", db.VGrip1, 0)
+ self.VGrip1:SetNormalTexture("Interface\\Tooltips\\UI-Tooltip-Background")
+ self.VGrip1:SetHighlightTexture("Interface\\Tooltips\\UI-Tooltip-Background")
+ self.VGrip1:GetNormalTexture():SetVertexColor(1, 1, 1, 0.5)
+ self.VGrip1:GetHighlightTexture():SetVertexColor(1, 1, 1, 0.5)
+ self.VGrip1:SetScript("OnMouseDown", function(self)
+ if not db.Locked then self:SetScript("OnUpdate", movegrip1) end
+ end)
+ self.VGrip1:SetScript("OnMouseUp", function(self) self:SetScript("OnUpdate", nil) end)
+ self.VGrip1:SetScript("OnHide", self.VGrip1:GetScript("OnMouseUp"))
+ self.VGrip1:SetFrameLevel(self.BarList:GetFrameLevel() + 2)
+
+ -- Create label resizing vertical grip 2
+ self.VGrip2 = CreateFrame("Button", "OmenVResizeGrip2", self.BarList)
+ self.VGrip2:SetWidth(1)
+ self.VGrip2:SetPoint("TOPLEFT", self.BarList, "TOPLEFT", db.VGrip2, 0)
+ self.VGrip2:SetPoint("BOTTOMLEFT", self.BarList, "BOTTOMLEFT", db.VGrip2, 0)
+ self.VGrip2:SetNormalTexture("Interface\\Tooltips\\UI-Tooltip-Background")
+ self.VGrip2:SetHighlightTexture("Interface\\Tooltips\\UI-Tooltip-Background")
+ self.VGrip2:GetNormalTexture():SetVertexColor(1, 1, 1, 0.5)
+ self.VGrip2:GetHighlightTexture():SetVertexColor(1, 1, 1, 0.5)
+ self.VGrip2:SetScript("OnMouseDown", function(self)
+ if not db.Locked then self:SetScript("OnUpdate", movegrip2) end
+ end)
+ self.VGrip2:SetScript("OnMouseUp", self.VGrip1:GetScript("OnMouseUp"))
+ self.VGrip2:SetScript("OnHide", self.VGrip1:GetScript("OnMouseUp"))
+ self.VGrip2:SetFrameLevel(self.BarList:GetFrameLevel() + 2)
+
+ --[[self.FocusButton = CreateFrame("Button", "OmenFocusButton", self.Title, "OptionsButtonTemplate")
+ self.FocusButton:SetWidth(16)
+ self.FocusButton:SetHeight(16)
+ self.FocusButton:SetPoint("TOPRIGHT")
+ self.FocusButton:SetText("F")
+ self.FocusButton:SetScript("OnClick", function(self, button, down)
+ db.UseFocus = not db.UseFocus
+ if db.UseFocus then
+ self:GetFontString():SetTextColor(1, 0.82, 0, 1)
+ else
+ self:GetFontString():SetTextColor(0.5, 0.5, 0.5, 1)
+ end
+ Omen:UpdateBars()
+ end)
+ if db.UseFocus then
+ self.FocusButton:GetFontString():SetTextColor(1, 0.82, 0, 1)
+ else
+ self.FocusButton:GetFontString():SetTextColor(0.5, 0.5, 0.5, 1)
+ end]]
+
+ self.CreateFrames = nil
+end
+
+function Omen:OnInitialize()
+ -- Create savedvariables
+ self.db = LibStub("AceDB-3.0"):New("Omen3DB", defaults)
+ self.db.RegisterCallback(self, "OnProfileChanged", "OnProfileChanged")
+ self.db.RegisterCallback(self, "OnProfileCopied", "OnProfileChanged")
+ self.db.RegisterCallback(self, "OnProfileReset", "OnProfileChanged")
+ db = self.db.profile
+ self:SetSinkStorage(db.Warnings.SinkOptions)
+
+ LSM.RegisterCallback(self, "LibSharedMedia_Registered", "UpdateUsedMedia")
+
+ -- These 2 functions self GC after running
+ self:CreateFrames()
+ self:SetupOptions()
+
+ self:RegisterEvent("PLAYER_LOGIN")
+ self.OnInitialize = nil
+end
+
+function Omen:PLAYER_LOGIN()
+ -- We set up anchors here because we only want to do it once on
+ -- PLAYER_LOGIN, hence we don't do it in OnEnable() which triggers on
+ -- the same event as well as on every subsequent Enable()/Disable() calls.
+ -- It cannot be earlier than PLAYER_LOGIN because layout-cache.txt
+ -- is loaded just before this event fires.
+ self:SetAnchors(true)
+ self.Anchor:SetAlpha(db.Alpha)
+ self.Anchor:SetFrameStrata(strsub(db.FrameStrata, 3))
+ self.Anchor:SetClampedToScreen(db.ClampToScreen)
+ self:UpdateBackdrop()
+ self:UpdateTitleBar()
+ self:UpdateGrips()
+ self:UpdateClickThrough()
+ self:UpdateRaidClassColors()
+ self:ClearAll()
+ self:UnregisterEvent("PLAYER_LOGIN")
+ if not db.Shown then self.Anchor:Hide() end -- Auto-show/hide will override this later if enabled
+
+ -- Optional !ClassColors addon support
+ if CUSTOM_CLASS_COLORS then
+ CUSTOM_CLASS_COLORS:RegisterCallback("UpdateBars", self)
+ end
+
+ -- ConfigMode support
+ do
+ CONFIGMODE_CALLBACKS = CONFIGMODE_CALLBACKS or {}
+ local oldTestMode = testMode
+ local oldLocked = db.Locked
+ function CONFIGMODE_CALLBACKS.Omen(action)
+ if action == "ON" then
+ oldTestMode = testMode
+ oldLocked = db.Locked
+ testMode = true
+ db.Locked = false
+ Omen:Toggle(true)
+ elseif action == "OFF" then
+ testMode = oldTestMode
+ db.Locked = oldLocked
+ manualToggle = false
+ Omen:UpdateVisible()
+ end
+ Omen:UpdateGrips()
+ Omen:UpdateBars()
+ LibStub("AceConfigRegistry-3.0"):NotifyChange("Omen")
+ end
+ end
+
+ -- LDB launcher
+ if LDB then
+ OmenLauncher = LDB:NewDataObject("Omen", {
+ type = "launcher",
+ icon = "Interface\\AddOns\\Omen\\icon",
+ OnClick = function(clickedframe, button)
+ if button == "RightButton" then Omen:ShowConfig() else Omen:Toggle() end
+ end,
+ OnTooltipShow = function(tt)
+ tt:AddLine(self.defaultTitle)
+ tt:AddLine("|cffffff00" .. L["Click|r to toggle the Omen window"])
+ tt:AddLine("|cffffff00" .. L["Right-click|r to open the options menu"])
+ end,
+ })
+ if LDBIcon and not IsAddOnLoaded("Broker2FuBar") and not IsAddOnLoaded("FuBar") then
+ LDBIcon:Register("Omen", OmenLauncher, db.MinimapIcon)
+ end
+ end
+
+ -- Optional launcher support for LFBP-3.0 if present, this code is placed here so
+ -- that it runs after all other addons have loaded since we don't embed LFBP-3.0
+ -- Yes, this is one big hack since LFBP-3.0 is a Rock library, and we embed it
+ -- via Ace3. OnEmbedInitialize() needs to be called manually.
+ if LibStub:GetLibrary("LibFuBarPlugin-3.0", true) and not IsAddOnLoaded("FuBar2Broker") then
+ local LFBP = LibStub:GetLibrary("LibFuBarPlugin-3.0")
+ LibStub("AceAddon-3.0"):EmbedLibrary(self, "LibFuBarPlugin-3.0")
+ self:SetFuBarOption("tooltipType", "GameTooltip")
+ self:SetFuBarOption("hasNoColor", true)
+ self:SetFuBarOption("cannotDetachTooltip", true)
+ self:SetFuBarOption("hideWithoutStandby", true)
+ self:SetFuBarOption("iconPath", [[Interface\AddOns\Omen\icon]])
+ self:SetFuBarOption("hasIcon", true)
+ self:SetFuBarOption("defaultPosition", "RIGHT")
+ self:SetFuBarOption("tooltipHiddenWhenEmpty", true)
+ self:SetFuBarOption("configType", "None")
+ LFBP:OnEmbedInitialize(self)
+ function Omen:OnUpdateFuBarTooltip()
+ GameTooltip:AddLine(self.defaultTitle)
+ GameTooltip:AddLine("|cffffff00" .. L["Click|r to toggle the Omen window"])
+ GameTooltip:AddLine("|cffffff00" .. L["Right-click|r to open the options menu"])
+ end
+ function Omen:OnFuBarClick(button)
+ if button == "RightButton" then self:ShowConfig() else self:Toggle() end
+ end
+ self.optionsFrames["FuBar"] = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("Omen", L["FuBar Options"], self.versionstring, "FuBar")
+ self:UpdateFuBarSettings()
+ end
+
+ self.PLAYER_LOGIN = nil
+end
+
+function Omen:OnEnable()
+ self:RegisterEvent("UNIT_THREAT_LIST_UPDATE")
+ self:RegisterEvent("UNIT_THREAT_SITUATION_UPDATE")
+ self:RegisterEvent("PLAYER_TARGET_CHANGED")
+
+ self:RegisterEvent("PARTY_MEMBERS_CHANGED")
+ self:RegisterEvent("UNIT_PET", "PARTY_MEMBERS_CHANGED")
+ self:RegisterEvent("UNIT_NAME_UPDATE", "PARTY_MEMBERS_CHANGED")
+ self:RegisterEvent("PLAYER_PET_CHANGED", "PARTY_MEMBERS_CHANGED")
+ --self:RegisterEvent("RAID_ROSTER_UPDATE", "PARTY_MEMBERS_CHANGED") -- Is this needed?
+
+ self:RegisterEvent("PLAYER_UPDATE_RESTING", "UpdateVisible")
+ self:RegisterEvent("PLAYER_ENTERING_WORLD")
+
+ if db.ShowWith.HideWhenOOC then
+ self:RegisterEvent("PLAYER_REGEN_DISABLED", "UpdateVisible")
+ self:RegisterEvent("PLAYER_REGEN_ENABLED", "UpdateVisible")
+ end
+ if db.UseFocus then
+ self:RegisterEvent("UNIT_TARGET")
+ end
+
+ self:PARTY_MEMBERS_CHANGED()
+ self:PLAYER_TARGET_CHANGED()
+end
+
+function Omen:OnDisable()
+ -- Cancel all timers (well at least nil them all
+ -- out in timers[], since AceTimer-3.0 cancels
+ -- them all OnDisable anyway).
+ for k, v in pairs(timers) do
+ self:CancelTimer(v, true)
+ timers[k] = nil
+ end
+
+ self:_toggle(false)
+end
+
+function Omen:OnProfileChanged(event, database, newProfileKey)
+ db = database.profile
+ self:SetAnchors(true)
+ self.Anchor:SetAlpha(db.Alpha)
+ self.Anchor:SetFrameStrata(strsub(db.FrameStrata, 3))
+ self.Anchor:SetClampedToScreen(db.ClampToScreen)
+ self:UpdateBackdrop()
+ self:UpdateTitleBar()
+ self:UpdateGrips()
+ self:ResizeBars()
+ self:ReAnchorBars()
+ self:ReAnchorLabels()
+ self:UpdateBarLabelSettings()
+ self:UpdateBarTextureSettings()
+ self:UpdateClickThrough()
+ self:UpdateRaidClassColors()
+ self:UpdateFuBarSettings()
+ -- These remainder settings were not placed in functions
+ -- and were just updated directly from the config code.
+ if LDBIcon and not IsAddOnLoaded("Broker2FuBar") and not IsAddOnLoaded("FuBar") then
+ LDBIcon:Refresh("Omen", db.MinimapIcon)
+ end
+ if db.ShowWith.HideWhenOOC then
+ self:RegisterEvent("PLAYER_REGEN_DISABLED", "UpdateVisible")
+ self:RegisterEvent("PLAYER_REGEN_ENABLED", "UpdateVisible")
+ else
+ self:UnregisterEvent("PLAYER_REGEN_DISABLED")
+ self:UnregisterEvent("PLAYER_REGEN_ENABLED")
+ end
+ if db.UseFocus then
+ self:RegisterEvent("UNIT_TARGET")
+ else
+ self:UnregisterEvent("UNIT_TARGET")
+ end
+ local f = self.TPSUpdateFrame
+ if f then
+ if db.Bar.ShowTPS then f:Show() else f:Hide() end
+ end
+ if db.Bar.ShowValue and db.Bar.ShowPercent then
+ bars[0].Text2:SetText(L["Threat [%]"])
+ else
+ bars[0].Text2:SetText(L["Threat"])
+ end
+
+ self:UpdateVisible()
+ self:UpdateBars()
+end
+
+function Omen:UpdateUsedMedia(event, mediatype, key)
+ if mediatype == "statusbar" then
+ if key == db.Bar.Texture then self:UpdateBarTextureSettings() end
+ elseif mediatype == "font" then
+ if key == db.TitleBar.Font then self:UpdateTitleBar() end
+ if key == db.Bar.Font then self:UpdateBarLabelSettings() self:UpdateBars() end
+ elseif mediatype == "background" then
+ if key == db.Background.Texture then self:UpdateBackdrop() end
+ elseif mediatype == "border" then
+ if key == db.Background.BorderTexture then self:UpdateBackdrop() end
+ --elseif mediatype == "sound" then
+ -- Do nothing
+ end
+end
+
+function Omen:SetAnchors(useDB)
+ local x, y, w, h
+
+ -- Set the scale, since the scaling affects the position
+ self.Anchor:SetScale(db.Scale)
+ self.VGrip1:SetWidth(1 / self.VGrip1:GetEffectiveScale())
+ self.VGrip2:SetWidth(1 / self.VGrip2:GetEffectiveScale())
+
+ -- Get position
+ if useDB then
+ x, y = db.PositionX, db.PositionY
+ if not x or not y then
+ self.Anchor:ClearAllPoints()
+ self.Anchor:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
+ x, y = self.Anchor:GetLeft(), db.GrowUp and self.Anchor:GetBottom() or self.Anchor:GetTop()
+ end
+ else
+ x, y = self.Anchor:GetLeft(), db.GrowUp and self.Anchor:GetBottom() or self.Anchor:GetTop()
+ end
+
+ -- Get width/height
+ w = useDB and db.PositionW or self.Anchor:GetWidth()
+ h = useDB and db.PositionH or self.Anchor:GetHeight()
+
+ -- Set the anchors and size
+ self.Anchor:ClearAllPoints()
+ self.Anchor:SetPoint(db.GrowUp and "BOTTOMLEFT" or "TOPLEFT", UIParent, "BOTTOMLEFT", x, y)
+ self.Anchor:SetWidth(w)
+ self.Anchor:SetHeight(h)
+ self.Anchor:SetUserPlaced(nil)
+
+ -- Save the data
+ db.PositionX, db.PositionY = x, y
+ db.PositionW, db.PositionH = w, h
+end
+
+-- For public use
+function Omen:Toggle(setting)
+ -- Don't set the manualToggle flag if "Hide Omen on 0 bars" option is active
+ if not (db.Autocollapse and db.CollapseHide) then
+ manualToggle = true
+ end
+ return self:_toggle(setting)
+end
+
+-- For internal use
+function Omen:_toggle(setting)
+ if setting == nil then
+ setting = not self.Anchor:IsShown()
+ end
+ if setting then
+ self.Anchor:Show()
+ else
+ self.Anchor:Hide()
+ end
+end
+
+function Omen:UpdateVisible(event)
+ local t = db.ShowWith
+ if not t.UseShowWith or manualToggle then return end
+
+ -- Hide if HideWhenOOC option is on, we're not in combat, and the triggering event is not
+ -- "PLAYER_REGEN_DISABLED" (we're out of combat during this event just before entering combat)
+ if t.HideWhenOOC and not InCombatLockdown() and event ~= "PLAYER_REGEN_DISABLED" then
+ self:_toggle(false)
+ return
+ end
+
+ -- Check for pet|party|raid|alone
+ local show = (t.Pet and UnitExists("pet")) or
+ (t.Party and inParty) or
+ (t.Raid and inRaid) or
+ (t.Alone and not inParty and not inRaid and not UnitExists("pet"))
+
+ -- Then hide override if necessary for resting|pvp
+ local inInstance, instanceType = IsInInstance()
+ if (t.HideWhileResting and IsResting()) or (t.HideInPVP and (instanceType == "pvp" or instanceType == "arena")) then
+ show = false
+ end
+
+ -- Hide if Autocollapse and Hide Omen on 0 Bars are both active and there are 0 bars.
+ if db.Autocollapse and db.CollapseHide and self.BarList.barsShown == 0 then
+ show = false
+ end
+
+ self:_toggle(show)
+end
+
+local bgFrame = {insets = {}}
+function Omen:UpdateBackdrop()
+ bgFrame.bgFile = LSM:Fetch("background", db.Background.Texture)
+ bgFrame.edgeFile = LSM:Fetch("border", db.Background.BorderTexture)
+ bgFrame.tile = db.Background.Tile
+ bgFrame.tileSize = db.Background.TileSize
+ bgFrame.edgeSize = db.Background.EdgeSize
+ local inset = floor(db.Background.EdgeSize / 4)
+ bgFrame.insets.left = inset
+ bgFrame.insets.right = inset
+ bgFrame.insets.top = inset
+ bgFrame.insets.bottom = inset
+ self.BarList:SetBackdrop(bgFrame)
+ if not db.TitleBar.UseSameBG then
+ bgFrame.bgFile = LSM:Fetch("background", db.TitleBar.Texture)
+ bgFrame.edgeFile = LSM:Fetch("border", db.TitleBar.BorderTexture)
+ bgFrame.tile = db.TitleBar.Tile
+ bgFrame.tileSize = db.TitleBar.TileSize
+ bgFrame.edgeSize = db.TitleBar.EdgeSize
+ local inset = floor(db.TitleBar.EdgeSize / 4)
+ bgFrame.insets.left = inset
+ bgFrame.insets.right = inset
+ bgFrame.insets.top = inset
+ bgFrame.insets.bottom = inset
+
+ end
+ self.Title:SetBackdrop(bgFrame)
+
+ local c = db.Background.Color
+ self.BarList:SetBackdropColor(c.r, c.g, c.b, c.a)
+ if not db.TitleBar.UseSameBG then c = db.TitleBar.Color end
+ self.Title:SetBackdropColor(c.r, c.g, c.b, c.a)
+
+ c = db.Background.BorderColor
+ self.BarList:SetBackdropBorderColor(c.r, c.g, c.b, c.a)
+ if not db.TitleBar.UseSameBG then c = db.TitleBar.BorderColor end
+ self.Title:SetBackdropBorderColor(c.r, c.g, c.b, c.a)
+
+ local h = db.Background.EdgeSize * 2
+ if not db.TitleBar.UseSameBG then h = db.TitleBar.EdgeSize * 2 end
+ self.Anchor:SetMinResize(90, h)
+ self.Title:SetMinResize(90, h)
+ if not db.TitleBar.ShowTitleBar then
+ self.Title:SetHeight(1e-6) -- See comment in Omen:UpdateTitleBar()
+ elseif h > db.TitleBar.Height then
+ self.Title:SetHeight(h)
+ else
+ self.Title:SetHeight(db.TitleBar.Height)
+ end
+ if self.Options then
+ self.Options.args.TitleBar.args.Height.min = h
+ end
+
+ --self.FocusButton:SetPoint("TOPRIGHT", -inset, -inset)
+
+ self.BarList:ClearAllPoints() -- See comment in Omen:UpdateTitleBar()
+ self.BarList:SetPoint("TOPLEFT", self.Title, "BOTTOMLEFT")
+ self.BarList:SetPoint("BOTTOMRIGHT", self.Anchor, "BOTTOMRIGHT")
+
+ self:ResizeBars()
+ self:ReAnchorBars()
+ self:UpdateBars()
+end
+
+function Omen:UpdateTitleBar()
+ local font = LSM:Fetch("font", db.TitleBar.Font)
+ local size = db.TitleBar.FontSize
+ local flags = db.TitleBar.FontOutline
+ local color = db.TitleBar.FontColor
+ self.TitleText:SetFont(font, size, flags)
+ self.TitleText:SetTextColor(color.r, color.g, color.b, color.a)
+ local h = db.Background.EdgeSize * 2
+ if not db.TitleBar.UseSameBG then h = db.TitleBar.EdgeSize * 2 end
+ if not db.TitleBar.ShowTitleBar then
+ -- Yes, its a hack, since it can't be set to 0
+ self.Title:SetHeight(1e-6)
+ self.Title:Hide()
+ elseif h > db.TitleBar.Height then
+ self.Title:SetHeight(h)
+ self.Title:Show()
+ else
+ self.Title:SetHeight(db.TitleBar.Height)
+ self.Title:Show()
+ end
+ -- This forces the UI to redraw it, I couldn't find a better way. Although it is
+ -- anchored to the Title, it doesn't update automatically on the height change.
+ self.BarList:ClearAllPoints()
+ self.BarList:SetPoint("TOPLEFT", self.Title, "BOTTOMLEFT")
+ self.BarList:SetPoint("BOTTOMRIGHT", self.Anchor, "BOTTOMRIGHT")
+end
+
+function Omen:UpdateFuBarSettings()
+ if LibStub:GetLibrary("LibFuBarPlugin-3.0", true) then
+ if db.FuBar.HideMinimapButton then
+ self:Hide()
+ else
+ self:Show()
+ if self:IsFuBarMinimapAttached() ~= db.FuBar.AttachMinimap then
+ self:ToggleFuBarMinimapAttached()
+ end
+ end
+ end
+end
+
+function Omen:UpdateGrips()
+ self.VGrip1:ClearAllPoints()
+ self.VGrip1:SetPoint("TOPLEFT", self.BarList, "TOPLEFT", db.VGrip1, 0)
+ self.VGrip1:SetPoint("BOTTOMLEFT", self.BarList, "BOTTOMLEFT", db.VGrip1, 0)
+ self.VGrip2:ClearAllPoints()
+ self.VGrip2:SetPoint("TOPLEFT", self.BarList, "TOPLEFT", db.VGrip2, 0)
+ self.VGrip2:SetPoint("BOTTOMLEFT", self.BarList, "BOTTOMLEFT", db.VGrip2, 0)
+ if db.Locked then
+ self.Grip:Hide()
+ self.VGrip1:Hide()
+ self.VGrip2:Hide()
+ else
+ self.Grip:Show()
+ self.VGrip1:Show()
+ if db.Bar.ShowTPS then
+ self.VGrip2:Show()
+ else
+ self.VGrip2:Hide()
+ end
+ end
+end
+
+function Omen:ToggleFocus()
+ db.UseFocus = not db.UseFocus
+ if db.UseFocus then
+ Omen:RegisterEvent("UNIT_TARGET")
+ else
+ Omen:UnregisterEvent("UNIT_TARGET")
+ end
+ Omen:UpdateBars()
+end
+
+function Omen:UpdateRaidClassColors()
+ if CUSTOM_CLASS_COLORS and db.Bar.UseCustomClassColors then
+ RAID_CLASS_COLORS = CUSTOM_CLASS_COLORS
+ else
+ RAID_CLASS_COLORS = _G.RAID_CLASS_COLORS
+ end
+ Omen:UpdateBars()
+end
+
+
+-----------------------------------------------------------------------------
+-- Omen warnings
+
+function Omen:Flash()
+ if not self.FlashFrame then
+ local flasher = CreateFrame("Frame", "OmenFlashFrame")
+ flasher:SetToplevel(true)
+ flasher:SetFrameStrata("FULLSCREEN_DIALOG")
+ flasher:SetAllPoints(UIParent)
+ flasher:EnableMouse(false)
+ flasher:Hide()
+ flasher.texture = flasher:CreateTexture(nil, "BACKGROUND")
+ flasher.texture:SetTexture("Interface\\FullScreenTextures\\LowHealth")
+ flasher.texture:SetAllPoints(UIParent)
+ flasher.texture:SetBlendMode("ADD")
+ flasher:SetScript("OnShow", function(self)
+ self.elapsed = 0
+ self:SetAlpha(0)
+ end)
+ flasher:SetScript("OnUpdate", function(self, elapsed)
+ elapsed = self.elapsed + elapsed
+ if elapsed < 2.6 then
+ local alpha = elapsed % 1.3
+ if alpha < 0.15 then
+ self:SetAlpha(alpha / 0.15)
+ elseif alpha < 0.9 then
+ self:SetAlpha(1 - (alpha - 0.15) / 0.6)
+ else
+ self:SetAlpha(0)
+ end
+ else
+ self:Hide()
+ end
+ self.elapsed = elapsed
+ end)
+ self.FlashFrame = flasher
+ end
+
+ self.FlashFrame:Show()
+end
+
+-- This function is adapted from Omen2 to be self-contained,
+-- which was initially taken from BigWigs
+function Omen:Shake()
+ local shaker = self.ShakerFrame
+ if not shaker then
+ shaker = CreateFrame("Frame", "OmenShaker", UIParent)
+ shaker:Hide()
+ shaker:SetScript("OnUpdate", function(self, elapsed)
+ elapsed = self.elapsed + elapsed
+ local x, y = 0, 0 -- Resets to original position if we're supposed to stop.
+ if elapsed >= 0.8 then
+ self:Hide()
+ else
+ x, y = random(-8, 8), random(-8, 8)
+ end
+ if WorldFrame:IsProtected() and InCombatLockdown() then
+ if not shaker.fail then
+ Omen:Print(L["|cffff0000Error:|r Omen cannot use shake warning if you have turned on nameplates at least once since logging in."])
+ shaker.fail = true
+ end
+ self:Hide()
+ else
+ WorldFrame:ClearAllPoints()
+ for i = 1, #self.originalPoints do
+ local v = self.originalPoints[i]
+ WorldFrame:SetPoint(v[1], v[2], v[3], v[4] + x, v[5] + y)
+ end
+ end
+ self.elapsed = elapsed
+ end)
+ shaker:SetScript("OnShow", function(self)
+ -- Store old worldframe positions, we need them all, people have frame modifiers for it
+ if not self.originalPoints then
+ self.originalPoints = {}
+ for i = 1, WorldFrame:GetNumPoints() do
+ tinsert(self.originalPoints, {WorldFrame:GetPoint(i)})
+ end
+ end
+ self.elapsed = 0
+ end)
+ self.ShakerFrame = shaker
+ end
+
+ shaker:Show()
+end
+
+function Omen:Warn(sound, flash, shake, message)
+ if sound then PlaySoundFile(LSM:Fetch("sound", db.Warnings.SoundFile)) end
+ if flash then self:Flash() end
+ if shake then self:Shake() end
+ if message then self:Pour(message, 1, 0, 0, nil, 24, "OUTLINE", true) end
+end
+
+
+-----------------------------------------------------------------------------
+-- Omen bar stuff
+
+do
+ -- OnUpdate function for bar animation, lasts 0.25 seconds
+ local function animate(self, elapsed)
+ local t = self.animationCursor + elapsed
+ local animData = self.animData
+ if t >= 0.25 then
+ self.texture:SetWidth(animData[1])
+ animData[3] = nil
+ animData[2] = nil
+ animData[1] = nil
+ t = 0
+ self:SetScript("OnUpdate", nil)
+ else
+ self.texture:SetWidth(animData[2] + animData[3] * t / 0.25)
+ end
+ self.animationCursor = t
+ end
+
+ -- function to start bar animations
+ local function AnimateTo(self, val)
+ if val == 1/0 or val == -1/0 then return end -- infinity, do nothing
+ if val == 0 then val = 1 end -- at least 1 pixel width
+ local animData = self.animData
+ if animData[1] == val then return end -- there is already an animation to the target width
+ local currentWidth = self.texture:GetWidth()
+ --if currentWidth > self:GetWidth() then currentWidth = self:GetWidth() end
+ if val == currentWidth then return end -- the current width is already the target width
+ animData[1] = val
+ animData[2] = currentWidth
+ animData[3] = val - currentWidth
+ self.animationCursor = 0
+ self:SetScript("OnUpdate", animate)
+ end
+
+ -- Create bars on demand
+ setmetatable(bars, {__index = function(self, barID)
+ local bar = CreateFrame("Frame", nil, Omen.BarList)
+ self[barID] = bar
+
+ local inset = db.Background.BarInset
+ local inset2 = db.Background.BarInset * 2
+ local color = db.Bar.InvertColors and db.Bar.BarColor or db.Bar.FontColor
+
+ bar:SetWidth(Omen.Anchor:GetWidth() - inset2)
+ bar:SetHeight(db.Bar.Height)
+ if db.Bar.ShowHeadings then
+ bar:SetPoint("TOPLEFT", Omen.BarList, "TOPLEFT", inset, -inset + (barID) * -(db.Bar.Height + db.Bar.Spacing))
+ else
+ bar:SetPoint("TOPLEFT", Omen.BarList, "TOPLEFT", inset, -inset + (barID-1) * -(db.Bar.Height + db.Bar.Spacing))
+ end
+
+ bar.Text1 = bar:CreateFontString(nil, nil, "GameFontNormalSmall")
+ bar.Text1:SetPoint("LEFT", bar, "LEFT", 5, 1)
+ bar.Text1:SetJustifyH("LEFT")
+ bar.Text1:SetFont(LSM:Fetch("font", db.Bar.Font), db.Bar.FontSize, db.Bar.FontOutline)
+ bar.Text1:SetTextColor(color.r, color.g, color.b, color.a)
+ bar.Text1:SetWidth(db.VGrip1 - 5)
+ bar.Text1:SetHeight(db.Bar.FontSize)
+ bar.Text1:SetNonSpaceWrap(false)
+
+ bar.Text2 = bar:CreateFontString(nil, nil, "GameFontNormalSmall")
+ bar.Text2:SetPoint("RIGHT", bar, "RIGHT", -5, 1)
+ bar.Text2:SetJustifyH("RIGHT")
+ bar.Text2:SetFont(LSM:Fetch("font", db.Bar.Font), db.Bar.FontSize, db.Bar.FontOutline)
+ bar.Text2:SetTextColor(color.r, color.g, color.b, color.a)
+ if db.Bar.ShowTPS then
+ bar.Text2:SetWidth(Omen.BarList:GetWidth() - db.VGrip2 - 5)
+ else
+ bar.Text2:SetWidth(Omen.BarList:GetWidth() - db.VGrip1 - 5)
+ end
+ bar.Text2:SetHeight(db.Bar.FontSize)
+ bar.Text2:SetNonSpaceWrap(false)
+
+ bar.Text3 = bar:CreateFontString(nil, nil, "GameFontNormalSmall")
+ bar.Text3:SetPoint("LEFT", bar.Text1, "RIGHT", 0, 0)
+ bar.Text3:SetJustifyH("RIGHT")
+ bar.Text3:SetFont(LSM:Fetch("font", db.Bar.Font), db.Bar.FontSize, db.Bar.FontOutline)
+ bar.Text3:SetTextColor(color.r, color.g, color.b, color.a)
+ bar.Text3:SetWidth(db.VGrip2 - db.VGrip1 - 5)
+ bar.Text3:SetHeight(db.Bar.FontSize)
+ bar.Text3:SetNonSpaceWrap(false)
+ if not db.Bar.ShowTPS then bar.Text3:Hide() end
+
+ bar.texture = bar:CreateTexture()
+ bar.texture:SetTexture(LSM:Fetch("statusbar", db.Bar.Texture))
+ bar.texture:SetPoint("TOPLEFT", bar, "TOPLEFT")
+ bar.texture:SetPoint("BOTTOMLEFT", bar, "BOTTOMLEFT")
+ color = db.Bar.InvertColors and db.Bar.FontColor or db.Bar.BarColor
+ bar.texture:SetVertexColor(color.r, color.g, color.b, color.a)
+
+ bar.animData = {}
+ bar.animationCursor = 0
+ bar.AnimateTo = AnimateTo
+
+ if barID == 0 then
+ bar.Text1:SetText(L["Name"])
+ if db.Bar.ShowValue and db.Bar.ShowPercent then
+ bar.Text2:SetText(L["Threat [%]"])
+ else
+ bar.Text2:SetText(L["Threat"])
+ end
+ bar.Text3:SetText(L["TPS"])
+ color = db.Bar.InvertColors and db.Bar.FontColor or db.Bar.HeadingBGColor
+ bar.texture:SetVertexColor(color.r, color.g, color.b, color.a)
+ bar:Hide()
+ elseif barID == 1 then
+ -- Parent our TPS update frame to the first bar, so that TPS updates
+ -- updates happen when at least 1 bar (the first bar) is shown.
+ Omen.TPSUpdateFrame = CreateFrame("Frame", nil, bar)
+ Omen.TPSUpdateFrame:SetScript("OnUpdate", function(self, elapsed) Omen:UpdateTPS() end)
+ if not db.Bar.ShowTPS then Omen.TPSUpdateFrame:Hide() end
+ end
+
+ return bar
+ end})
+end
+
+function Omen:ResizeBars()
+ local inset = db.Background.BarInset * 2
+ local w = Omen.Anchor:GetWidth() - inset
+ for i = 0, #bars do
+ bars[i]:SetWidth(w)
+ bars[i]:SetHeight(db.Bar.Height)
+ end
+end
+
+function Omen:ReAnchorBars()
+ local inset = db.Background.BarInset
+ if db.Bar.ShowHeadings then
+ for i = 0, #bars do
+ bars[i]:SetPoint("TOPLEFT", self.BarList, "TOPLEFT", inset, -inset + (i) * -(db.Bar.Height + db.Bar.Spacing))
+ end
+ else
+ for i = 1, #bars do
+ bars[i]:SetPoint("TOPLEFT", self.BarList, "TOPLEFT", inset, -inset + (i-1) * -(db.Bar.Height + db.Bar.Spacing))
+ end
+ bars[0]:Hide()
+ end
+end
+
+function Omen:UpdateBarLabelSettings()
+ local font = LSM:Fetch("font", db.Bar.Font)
+ local size = db.Bar.FontSize
+ local flags = db.Bar.FontOutline
+ local color = db.Bar.InvertColors and db.Bar.BarColor or db.Bar.FontColor
+ local color2 = db.Bar.InvertColors and db.Bar.FontColor or db.Bar.BarColor
+ for i = 0, #bars do
+ bars[i].Text1:SetFont(font, size, flags)
+ bars[i].Text2:SetFont(font, size, flags)
+ bars[i].Text3:SetFont(font, size, flags)
+ bars[i].Text1:SetTextColor(color.r, color.g, color.b, color.a)
+ bars[i].Text2:SetTextColor(color.r, color.g, color.b, color.a)
+ bars[i].Text3:SetTextColor(color.r, color.g, color.b, color.a)
+ bars[i].Text1:SetHeight(size)
+ bars[i].Text2:SetHeight(size)
+ bars[i].Text3:SetHeight(size)
+ bars[i].texture:SetVertexColor(color2.r, color2.g, color2.b, color2.a)
+ end
+ color = db.Bar.InvertColors and db.Bar.FontColor or db.Bar.HeadingBGColor
+ color2 = db.Bar.InvertColors and db.Bar.HeadingBGColor or db.Bar.FontColor
+ bars[0].texture:SetVertexColor(color.r, color.g, color.b, color.a)
+ bars[0].Text1:SetTextColor(color2.r, color2.g, color2.b, color2.a)
+ bars[0].Text2:SetTextColor(color2.r, color2.g, color2.b, color2.a)
+ bars[0].Text3:SetTextColor(color2.r, color2.g, color2.b, color2.a)
+end
+
+function Omen:ReAnchorLabels()
+ local w = db.VGrip1
+ local w2 = db.Bar.ShowTPS and Omen.BarList:GetWidth() - db.VGrip2 or Omen.BarList:GetWidth() - w
+ local w3 = db.VGrip2 - db.VGrip1
+ for i = 0, #bars do
+ bars[i].Text1:SetWidth(w - 5)
+ bars[i].Text2:SetWidth(w2 - 5)
+ if db.Bar.ShowTPS then
+ bars[i].Text3:SetWidth(w3 - 5)
+ bars[i].Text3:Show()
+ else
+ bars[i].Text3:Hide()
+ end
+ end
+end
+
+function Omen:UpdateBarTextureSettings()
+ local texturepath = LSM:Fetch("statusbar", db.Bar.Texture)
+ for i = 0, #bars do
+ bars[i].texture:SetTexture(texturepath)
+ end
+end
+
+function Omen:UpdateClickThrough()
+ self.Title:EnableMouse(not db.ClickThrough)
+ self.BarList:EnableMouse(not db.ClickThrough)
+end
+
+
+-----------------------------------------------------------------------------
+-- Omen event functions
+
+-- Fired when a mob has its threat list updated. The mob that
+-- had its list updated is the first parameter of the event.
+function Omen:UNIT_THREAT_LIST_UPDATE(event, unitID)
+ -- It appears that unitID can only be "target" or "focus"
+ self:UpdateBars()
+end
+
+-- Fired when a unit's threat situation changes. The unit that
+-- had a change in threat situation is the first parameter of
+-- the event. Note that this only triggers when major state
+-- changes, not when the raw threat values change.
+function Omen:UNIT_THREAT_SITUATION_UPDATE(...)
+ self:UpdateBars()
+end
+
+function Omen:PLAYER_TARGET_CHANGED()
+ -- Stop our unit update timer for updating threat on "targettarget"
+ if timers.UpdateBars then
+ self:CancelTimer(timers.UpdateBars, true)
+ timers.UpdateBars = nil
+ end
+ self:UpdateBars()
+end
+
+function Omen:UNIT_TARGET(event, unitID)
+ if unitID == "focus" and db.UseFocus and self.unitID == "focustarget" then
+ self:UpdateBars()
+ end
+end
+
+local lastPartyUpdateTime = GetTime()
+
+function Omen:PARTY_MEMBERS_CHANGED()
+ local oldInParty, oldInRaid = inParty, inRaid
+ inParty = GetNumPartyMembers() > 0
+ inRaid = GetNumRaidMembers() > 0
+ if oldInParty ~= inParty or oldInRaid ~= inRaid then manualToggle = false end
+ self:UpdateVisible()
+
+ -- Run the update if the last call is more than 0.5 seconds ago else
+ -- schedule an update 0.5 seconds later if one isn't already scheduled
+ if GetTime() - lastPartyUpdateTime > 0.5 then
+ self:UpdatePartyGUIDs()
+ elseif not timers.UpdatePartyGUIDs then
+ timers.UpdatePartyGUIDs = self:ScheduleTimer("UpdatePartyGUIDs", 0.5)
+ end
+end
+
+-- This function updates the name and class guid lookup tables of the raid
+function Omen:UpdatePartyGUIDs()
+ lastPartyUpdateTime = GetTime()
+ if timers.UpdatePartyGUIDs then
+ self:CancelTimer(timers.UpdatePartyGUIDs, true)
+ timers.UpdatePartyGUIDs = nil
+ end
+
+ local _
+ local me = UnitGUID("player")
+ wipe(guidClassLookup)
+ if me then -- Because it sometimes is nil on zoning/logging in.
+ guidNameLookup[me] = UnitName("player")
+ _, guidClassLookup[me] = UnitClass("player")
+ else
+ timers.UpdatePartyGUIDs = self:ScheduleTimer("UpdatePartyGUIDs", 0.5)
+ end
+ if UnitExists("pet") then
+ local petGUID = UnitGUID("pet")
+ guidClassLookup[petGUID] = "PET"
+ guidNameLookup[petGUID] = UnitName("pet")--.." ["..UnitName("player").."]"
+ end
+
+ if inParty or inRaid then
+ local playerFmt = inRaid and rID or pID
+ local petFmt = inRaid and rpID or ppID
+ local currentPartySize = inRaid and GetNumRaidMembers() or GetNumPartyMembers()
+
+ for i = 1, currentPartySize do
+ local unitID = playerFmt[i]
+ local pGUID = UnitGUID(unitID)
+
+ if pGUID then
+ guidNameLookup[pGUID] = UnitName(unitID)
+ _, guidClassLookup[pGUID] = UnitClass(unitID)
+
+ -- lookup pet (if existing)
+ local petID = petFmt[i]
+ local petGUID = UnitGUID(petID)
+ if petGUID then
+ guidNameLookup[petGUID] = UnitName(petID)--.." ["..UnitName(unitID).."]"
+ guidClassLookup[petGUID] = "PET"
+ end
+ end
+ end
+ end
+ guidNameLookup["AGGRO"] = L["> Pull Aggro <"]
+ guidClassLookup["AGGRO"] = "AGGRO"
+end
+
+function Omen:PLAYER_ENTERING_WORLD()
+ manualToggle = false
+ wipe(guidNameLookup)
+ self:PARTY_MEMBERS_CHANGED()
+end
+
+
+-----------------------------------------------------------------------------
+-- Omen update functions
+
+--[[
+First, some definitions:
+* mob - enemy creature
+* threat list - a mob's list of possible targets, along with each possible target's current threat value
+* threat situation - the situation that a unit is currently in (either globally, or with respect to a certain mob)
+* scaled percentage - a threat percentage, where 100% means you will pull aggro (become the primary target of the mob), and thus this % cannot be higher than 100% under normal circumstances
+* raw threat percentage - the percentage of the units threat when divided by the threat of the mob's current primary target, this % CAN be over 100%
+---------
+state = UnitThreatSituation(unit, mob)
+
+Returns the unit's threat situation with respect to the given mob. The state can be one of the following values:
+nil = the unit is not on the mob's threat list
+0 = 0-99% raw threat percentage (no indicator shown)
+1 = 100% or more raw threat percentage (yellow warning indicator shown)
+2 = tanking, other has 100% or more raw threat percentage (orange indicator shown)
+3 = tanking, all others have less than 100% raw percentage threat (red indicator shown)
+---------
+state = UnitThreatSituation(unit)
+
+Returns the unit's maximum threat state on any mob's threat list.
+---------
+isTanking, state, scaledPercent, rawPercent, threatValue = UnitDetailedThreatSituation(unit, mob)
+
+Returns detailed information about the unit's state on the mob's threat list.
+isTanking is true if the unit the primary target of the mob (and by definition has 100% threat)
+state is the unit's threat situation, as listed above.
+scaledPercent is the current percent threat of the unit, scaled in the 0-100% range based on distance from target.
+rawPercent is the current percent threat of the unit relative to the primary target of the mob.
+threatValue is the amount of threat that the unit has on the mob's threat list. This is roughly approximate to the amount of damage and healing the unit has done.
+---------
+r, g, b = GetThreatStatusColor(state)
+
+Returns the colors used in the UI to represent each major threat state.
+]]
+
+local threatTable -- Format: threatTable[guid] = threatValue
+local sortTable = {} -- Format: threatTable[i] = guid -- used for sorting by sortfunction()
+local tankGUID -- Used to store which unit is tanking and hence has 100% threat by definition
+local topthreat -- Used to store the top threat value
+local lastWarn = { -- Used to store information for threat warnings
+ threatpercent = 0,
+}
+local threatStore = {} -- Format: threatStore[i] = threatTable[guid] -- used for storing past threatTables
+local threatStoreTime = {} -- Format: threatStoreTime[i] = GetTime()
+local negativeTable = {} -- Format: negativeTable[guid] = true -- stores if someone is under the effects of Fade or Mirror Image
+
+local function sortfunction(a, b)
+ return threatTable[a] > threatTable[b]
+end
+
+local function updatethreat(unitid, mobunitid)
+ local guid = UnitGUID(unitid)
+ if guid and not threatTable[guid] then
+ local isTanking, state, scaledPercent, rawPercent, threatValue = UnitDetailedThreatSituation(unitid, mobunitid)
+ if threatValue then
+ -- Threat can be negative due to temporary threat reduction effects such as Fade and Mirror Image (-410065408).
+ if threatValue < 0 then
+ threatValue = threatValue + 410065408
+ negativeTable[guid] = true
+ end
+ if threatValue > topthreat then topthreat = threatValue end
+ if isTanking then tankGUID = guid end
+ threatTable[guid] = threatValue
+ else
+ -- We use the special value -1 to indicate nil here.
+ threatTable[guid] = -1
+ end
+ end
+end
+
+local threatUnitIDFindList = {"target", "targettarget"}
+local threatUnitIDFindList2 = {"focus", "focustarget", "target", "targettarget"}
+function Omen:FindThreatMob()
+ -- Figure out which mob to show threat on.
+ -- It has to be attackable and not human controlled.
+ local t = db.UseFocus and threatUnitIDFindList2 or threatUnitIDFindList
+ local name, name2
+ for i = 1, #t do
+ local mob = t[i]
+ if UnitExists(mob) then
+ name2 = UnitName(mob)
+ guidNameLookup[UnitGUID(mob)] = name2
+ if not name then name = name2 end
+ if not UnitIsPlayer(mob) and UnitCanAttack("player", mob) and UnitHealth(mob) > 0 then
+ if not db.IgnorePlayerPets or not UnitPlayerControlled(mob) then
+ self.TitleText:SetText(name2)
+ self.unitID = mob
+ return mob
+ end
+ end
+ end
+ end
+ self.TitleText:SetText(name)
+ self.unitID = nil
+end
+
+-- Frame for throtling updates
+local OmenUpdateBarsThrotleFrame = CreateFrame("Frame")
+OmenUpdateBarsThrotleFrame:Hide()
+OmenUpdateBarsThrotleFrame:SetScript("OnUpdate", function(self, elapsed)
+ self:Hide()
+ Omen:UpdateBarsReal()
+end)
+function Omen:UpdateBars()
+ OmenUpdateBarsThrotleFrame:Show()
+end
+
+local queried = false
+function Omen:UpdateBarsReal()
+ if db.Autocollapse and db.CollapseHide then
+ -- Update the visibility because it could have been hidden on 0 bars
+ self.BarList.barsShown = 1 -- Dummy value
+ self:UpdateVisible()
+ end
+ if not self.Anchor:IsShown() then
+ self.BarList.barsShown = 0
+ return
+ end
+
+ local myGUID = UnitGUID("player")
+ local dbBar = db.Bar
+ local mob, mobGUID, mobTargetGUID
+ topthreat = -1
+
+ if testMode then
+ threatTable = newTable()
+ local key = next(showClassesOptionTable)
+ for i = 1, 25 do
+ if i == 22 and myGUID then -- Because I've got myGUID == nil before
+ threatTable[myGUID] = i*5000
+ else
+ threatTable[i] = i*5000
+ guidNameLookup[i] = showClassesOptionTable[key]
+ if key ~= "*NOTINPARTY*" then guidClassLookup[i] = key end
+ key = next(showClassesOptionTable, key) or next(showClassesOptionTable)
+ end
+ end
+ tankGUID = 25
+ topthreat = 25*5000
+ mob = ""
+ self.TitleText:SetText(L["Test Mode"])
+ else
+ mob = self:FindThreatMob()
+ if not mob then
+ self:ClearAll()
+ return
+ end
+ mobGUID = UnitGUID(mob)
+
+ -- Schedule a repeating timer for updating threat on "targettarget"
+ -- since we get no events on a targettarget change.
+ if mob == "targettarget" and not timers.UpdateBars then
+ timers.UpdateBars = self:ScheduleRepeatingTimer("UpdateBars", 0.5)
+ end
+
+ -- We want the mob's target just in case the tank isn't
+ -- in our raid (say an NPC or some other player)
+ local mobTarget = mob.."target"
+ local mobTargetGUID = UnitGUID(mobTarget)
+ if mobTargetGUID then
+ guidNameLookup[mobTargetGUID] = UnitName(mobTarget)
+ end
+
+ threatTable = newTable()
+ threatTable[mobGUID] = -1
+ tankGUID = nil
+ wipe(negativeTable)
+
+ -- Get data for threat on mob by scanning the whole raid
+ if inParty or inRaid then
+ if inRaid then
+ for i = 1, GetNumRaidMembers() do
+ updatethreat(rID[i], mob)
+ updatethreat(rpID[i], mob)
+ updatethreat(rtID[i], mob)
+ updatethreat(rptID[i], mob)
+ end
+ else
+ for i = 1, GetNumPartyMembers() do
+ updatethreat(pID[i], mob)
+ updatethreat(ppID[i], mob)
+ updatethreat(ptID[i], mob)
+ updatethreat(pptID[i], mob)
+ end
+ end
+
+ end
+ if not inRaid then
+ updatethreat("player", mob)
+ updatethreat("pet", mob)
+ updatethreat("target", mob)
+ updatethreat("pettarget", mob)
+ end
+ updatethreat("target", mob)
+ updatethreat("targettarget", mob)
+ updatethreat("focus", mob)
+ updatethreat("focustarget", mob)
+ updatethreat(mobTarget, mob)
+ updatethreat("mouseover", mob)
+ updatethreat("mouseovertarget", mob)
+ end
+ local tankThreat = tankGUID and threatTable[tankGUID] or mobTargetGUID and threatTable[mobTargetGUID] or topthreat
+ if dbBar.ShowAggroBar and tankThreat > 0 then
+ if GetItemInfo(37727) then -- 5 yards (Ruby Acorn - http://www.wowhead.com/?item=37727)
+ threatTable["AGGRO"] = tankThreat * (IsItemInRange(37727, mob) == 1 and 1.1 or 1.3)
+ else -- 9 yards compromise
+ threatTable["AGGRO"] = tankThreat * (CheckInteractDistance(mob, 3) and 1.1 or 1.3)
+ if not queried and not ItemRefTooltip:IsVisible() then
+ ItemRefTooltip:SetHyperlink("item:37727")
+ queried = true -- Only query once per session
+ end
+ end
+ end
+
+ -- Sort the threatTable
+ local i = 1
+ for k, v in pairs(threatTable) do
+ if v ~= -1 then
+ sortTable[i] = k
+ i = i + 1
+ end
+ end
+ for j = i, #sortTable do
+ sortTable[j] = nil
+ end
+ if #sortTable == 0 then
+ self:ClearAll()
+ self.TitleText:SetText(guidNameLookup[mobGUID])
+ return
+ end
+ sort(sortTable, sortfunction)
+
+ -- Now update the bars on screen
+ local inset = db.Background.BarInset * 2
+ local w = self.BarList:GetWidth() - inset
+ local h = self.BarList:GetHeight() - inset
+ topthreat = threatTable[sortTable[1]]
+ if topthreat == 0 then topthreat = 1 end -- To avoid 0/0 division
+ local showSelfYet = true
+
+ if dbBar.AlwaysShowSelf then
+ -- Check if we're one of the bars to be displayed
+ for j = 1, #sortTable do
+ if sortTable[j] == myGUID then
+ showSelfYet = false -- Yes, so flag it false
+ break
+ end
+ end
+ end
+
+ -- Check how many bars of space we have
+ local numBars = db.Autocollapse and db.NumBars or floor((h - dbBar.Height) / (dbBar.Height + dbBar.Spacing) + 1.01)
+
+ i = 1 -- Counts one higher than number of bars used
+ if dbBar.ShowHeadings then
+ if i <= numBars then
+ i = i + 1
+ bars[0].texture:SetWidth(w)
+ bars[0]:Show()
+ end
+ else
+ bars[0]:Hide()
+ end
+ for j = 1, #sortTable do
+ if i > numBars then break end
+ local guid = sortTable[j]
+ local class = guidClassLookup[guid]
+ local show = class == nil and dbBar.Classes["*NOTINPARTY*"] or class == "AGGRO" and dbBar.ShowAggroBar or dbBar.Classes[class]
+ if dbBar.AlwaysShowSelf and i == numBars and not showSelfYet and guid ~= myGUID then
+ show = false
+ end
+ if dbBar.AlwaysShowSelf and guid == myGUID then
+ show = true
+ showSelfYet = true
+ end
+ if show then
+ local bar = bars[dbBar.ShowHeadings and i-1 or i]
+ local threat = threatTable[guid]
+
+ -- Update the text on the bar
+ bar.Text1:SetText(guidNameLookup[guid])
+ if dbBar.ShowPercent and dbBar.ShowValue then
+ if dbBar.ShortNumbers and threat >= 100000 then
+ bar.Text2:SetFormattedText("%2.1fk [%d%%]", threat / 100000, tankThreat == 0 and 0 or threat / tankThreat * 100)
+ else
+ bar.Text2:SetFormattedText("%d [%d%%]", threat / 100, tankThreat == 0 and 0 or threat / tankThreat * 100)
+ end
+ elseif dbBar.ShowValue then
+ if dbBar.ShortNumbers and threat >= 100000 then
+ bar.Text2:SetFormattedText("%2.1fk", threat / 100000)
+ else
+ bar.Text2:SetFormattedText("%d", threat / 100)
+ end
+ else
+ bar.Text2:SetFormattedText("%d%%", tankThreat == 0 and 0 or threat / tankThreat * 100)
+ end
+
+ -- Update the color of the bar
+ local c = (negativeTable[guid] and dbBar.FadeBarColor) or
+ (guid == myGUID and dbBar.UseMyBarColor and dbBar.MyBarColor) or
+ (guid == tankGUID and dbBar.UseTankBarColor and dbBar.TankBarColor) or
+ (guid == "AGGRO" and dbBar.AggroBarColor) or
+ (dbBar.UseClassColors and (RAID_CLASS_COLORS[class] or (class == "PET" and dbBar.PetBarColor))) or
+ dbBar.BarColor
+ if dbBar.InvertColors then
+ bar.Text1:SetTextColor(c.r, c.g, c.b, c.a or 1)
+ bar.Text2:SetTextColor(c.r, c.g, c.b, c.a or 1)
+ bar.Text3:SetTextColor(c.r, c.g, c.b, c.a or 1)
+ else
+ bar.texture:SetVertexColor(c.r, c.g, c.b, c.a or 1)
+ end
+
+ -- Update the width of the bar, and animate if necessary
+ local width = w * threat / topthreat
+ if width <= 0 then width = 1 end
+ if dbBar.AnimateBars and self.Anchor.IsMovingOrSizing ~= 2 then
+ bar:AnimateTo(width)
+ else
+ bar.texture:SetWidth(width)
+ end
+
+ bar.guid = guid -- For TPS calcs
+ bar:Show()
+ i = i + 1
+ end
+ end
+ -- And hide the rest
+ for j = dbBar.ShowHeadings and i-1 or i, #bars do
+ bars[j]:Hide()
+ end
+ if db.Autocollapse then
+ self.Anchor:SetHeight((i-1)*dbBar.Height + (i-2)*dbBar.Spacing + self.Title:GetHeight() + inset)
+ end
+ self.BarList:Show()
+ self.BarList.barsShown = dbBar.ShowHeadings and i-2 or i-1
+
+ -- Threat warnings
+ if testMode then
+ threatTable = delTable(threatTable)
+ elseif myGUID then
+ local myClass = guidClassLookup[myGUID]
+ local myThreatPercent = threatTable[myGUID] / tankThreat * 100
+ local t = db.Warnings
+ if lastWarn.mobGUID == mobGUID and myThreatPercent >= t.Threshold and t.Threshold > lastWarn.threatpercent then
+ local shapeShiftForm = GetShapeshiftForm()
+ if not t.DisableWhileTanking or not (
+ myClass == "WARRIOR" and GetBonusBarOffset() == 2
+ or myClass == "DRUID" and GetBonusBarOffset() == 3
+ or myClass == "HERO" and GetBonusBarOffset() == 3
+ or UnitAura("player", GetSpellInfo(25780)) -- Righteous Fury
+ or UnitAura("player", GetSpellInfo(701463)) -- Mana-forged Barrier
+ or shapeShiftForm ~= 0 and (
+ GetShapeshiftFormInfo(shapeShiftForm) == "Interface\\Icons\\Ability_Warrior_DefensiveStance"
+ or GetShapeshiftFormInfo(shapeShiftForm) == "Interface\\Icons\\Spell_Deathknight_FrostPresence"
+ ))
+ then
+ self:Warn(t.Sound, t.Flash, t.Shake, t.Message and L["Passed %s%% of %s's threat!"]:format(t.Threshold, guidNameLookup[lastWarn.tankGUID]))
+ end
+ end
+ -- Remove TPS data if the last scanned mob is different
+ if lastWarn.mobGUID ~= mobGUID then
+ delTable(threatStore)
+ threatStore = newTable()
+ wipe(threatStoreTime)
+ end
+ tinsert(threatStore, threatTable)
+ tinsert(threatStoreTime, GetTime())
+ -- Store last scanned mob GUID
+ local u = tankGUID or mobTargetGUID or (dbBar.ShowAggroBar and sortTable[2] or sortTable[1])
+ if u ~= "AGGRO" then
+ lastWarn.mobGUID = mobGUID
+ lastWarn.tankGUID = u
+ lastWarn.threatpercent = myThreatPercent
+ end
+ threatTable = nil
+ end
+end
+
+function Omen:ClearAll()
+ for i = 0, #bars do
+ bars[i]:Hide()
+ end
+ self.TitleText:SetText(self.defaultTitle)
+ if db.Autocollapse then
+ self.Anchor:SetHeight(self.Title:GetHeight())
+ self.BarList:Hide()
+ if db.CollapseHide and not self.Anchor.IsMovingOrSizing and not manualToggle then
+ self.Anchor:Hide()
+ end
+ end
+ self.BarList.barsShown = 0
+ -- Store last scanned mob GUID
+ lastWarn.mobGUID = nil
+ lastWarn.tankGUID = nil
+ lastWarn.threatpercent = 0
+ -- Remove TPS data
+ delTable(threatStore)
+ threatStore = newTable()
+ wipe(threatStoreTime)
+ threatTable = nil
+end
+
+function Omen:UpdateTPS()
+ local numBars = #bars
+ if testMode then
+ if db.Bar.ShowAggroBar then
+ bars[1].Text3:SetText("--")
+ for i = 2, numBars do
+ bars[i].Text3:SetText(1300 - 50*(i-1))
+ end
+ else
+ for i = 1, numBars do
+ bars[i].Text3:SetText(1300 - 50*i)
+ end
+ end
+ return
+ end
+ -- Remove data that is too old
+ local TPSWindow = db.Bar.TPSWindow
+ local startTime = GetTime() - TPSWindow
+ while threatStoreTime[2] and startTime > threatStoreTime[2] do
+ delTable(tremove(threatStore, 1))
+ tremove(threatStoreTime, 1)
+ end
+ -- Now check that we still have enough data
+ local dataSize = #threatStoreTime
+ if dataSize == 0 or startTime <= threatStoreTime[1] then
+ -- We do not have enough data, TPSWindow seconds has not passed
+ for i = 1, numBars do
+ bars[i].Text3:SetText("??")
+ end
+ return
+ end
+ -- Check for special case with just 1 data point past TPSWindow seconds
+ if dataSize == 1 then
+ -- Threat generated is 0
+ for i = 1, numBars do
+ bars[i].Text3:SetText("0")
+ end
+ return
+ end
+ -- We have at least 2 data points
+ for i = 1, numBars do
+ local bar = bars[i]
+ if not bar:IsShown() then return end
+ local guid = bar.guid
+ if guid == "AGGRO" then
+ bar.Text3:SetText("--")
+ else
+ local baseThreat = threatStore[1][guid]
+ local secondThreat = threatStore[2][guid]
+ local finalThreat = threatStore[dataSize][guid]
+ if baseThreat and secondThreat and finalThreat then
+ -- Calculate TPS
+ local ratio = (startTime - threatStoreTime[1]) / (threatStoreTime[2] - threatStoreTime[1])
+ local startThreat = (secondThreat - baseThreat) * ratio + baseThreat
+ bar.Text3:SetFormattedText("%d", (finalThreat - startThreat) / TPSWindow / 100)
+ else
+ -- We don't have enough data for this unit
+ bar.Text3:SetText("??")
+ end
+ end
+ end
+end
+
+
+-----------------------------------------------------------------------------
+-- Title Right Click menu
+
+do
+ -- Upvalue the functions in the menu
+ local function updateGrip()
+ db.Locked = not db.Locked
+ Omen:UpdateGrips()
+ LibStub("AceConfigRegistry-3.0"):NotifyChange("Omen")
+ end
+ local function toggleFocus() Omen:ToggleFocus() end
+ local function toggleTestMode()
+ testMode = not testMode
+ Omen:UpdateBars()
+ LibStub("AceConfigRegistry-3.0"):NotifyChange("Omen")
+ end
+ local function showConfig() Omen:ShowConfig() end
+ local function toggle() Omen:Toggle() end
+
+ function Omen.TitleQuickMenu(self, level)
+ if not level then return end
+ local info = self.info
+ wipe(info)
+ if level == 1 then
+ -- Create the title of the menu
+ info.isTitle = 1
+ info.text = L["Omen Quick Menu"]
+ info.notCheckable = 1
+ UIDropDownMenu_AddButton(info, level)
+
+ info.disabled = nil
+ info.isTitle = nil
+ info.notCheckable = nil
+
+ info.text = L["Lock Omen"]
+ info.func = updateGrip
+ info.checked = db.Locked
+ info.tooltipTitle = L["Lock Omen"]
+ info.tooltipText = L["Locks Omen in place and prevents it from being dragged or resized."]
+ UIDropDownMenu_AddButton(info, level)
+
+ info.text = L["Use Focus Target"]
+ info.func = toggleFocus
+ info.checked = db.UseFocus
+ info.tooltipTitle = L["Use Focus Target"]
+ info.tooltipText = L["Tells Omen to additionally check your 'focus' and 'focustarget' before your 'target' and 'targettarget' in that order for threat display."]
+ UIDropDownMenu_AddButton(info, level)
+
+ info.text = L["Test Mode"]
+ info.func = toggleTestMode
+ info.checked = testMode
+ info.tooltipTitle = L["Test Mode"]
+ info.tooltipText = L["Tells Omen to enter Test Mode so that you can configure Omen's display much more easily."]
+ UIDropDownMenu_AddButton(info, level)
+
+ info.text = L["Open Config"]
+ info.func = showConfig
+ info.checked = nil
+ info.tooltipTitle = L["Open Config"]
+ info.tooltipText = L["Open Omen's configuration panel"]
+ UIDropDownMenu_AddButton(info, level)
+
+ info.text = L["Hide Omen"]
+ info.func = toggle
+ info.tooltipTitle = L["Hide Omen"]
+ info.tooltipText = nil
+ UIDropDownMenu_AddButton(info, level)
+
+ -- Close menu item
+ info.text = CLOSE
+ info.func = self.HideMenu
+ info.checked = nil
+ info.arg1 = nil
+ info.notCheckable = 1
+ info.tooltipTitle = CLOSE
+ UIDropDownMenu_AddButton(info, level)
+ end
+ end
+end
+
+
+-----------------------------------------------------------------------------
+-- Omen config stuff
+
+function Omen:SetupOptions()
+ LibStub("AceConfigRegistry-3.0"):RegisterOptionsTable("Omen", self.GenerateOptions)
+ LibStub("AceConfig-3.0"):RegisterOptionsTable("OmenSlashCommand", self.OptionsSlash, "omen")
+
+ -- The ordering here matters, it determines the order in the Blizzard Interface Options
+ local ACD3 = LibStub("AceConfigDialog-3.0")
+ self.optionsFrames = {}
+ self.optionsFrames.Omen = ACD3:AddToBlizOptions("Omen", self.versionstring, nil, "General")
+ self.optionsFrames.ShowWhen = ACD3:AddToBlizOptions("Omen", L["Show When..."], self.versionstring, "ShowWhen")
+ self.optionsFrames.ShowClasses = ACD3:AddToBlizOptions("Omen", L["Show Classes..."], self.versionstring, "ShowClasses")
+ self.optionsFrames.TitleBar = ACD3:AddToBlizOptions("Omen", L["Title Bar Settings"], self.versionstring, "TitleBar")
+ self.optionsFrames.Bars = ACD3:AddToBlizOptions("Omen", L["Bar Settings"], self.versionstring, "Bars")
+ self.optionsFrames.Warnings = ACD3:AddToBlizOptions("Omen", L["Warning Settings"], self.versionstring, "Warnings")
+ self:RegisterModuleOptions("OmenSlashCommand", self.OptionsSlash, L["Slash Command"])
+ self:RegisterModuleOptions("Profiles", function() return LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db) end, L["Profiles"])
+ self.optionsFrames.Help = ACD3:AddToBlizOptions("Omen", L["Help File"], self.versionstring, "Help")
+
+ self.SetupOptions = nil
+end
+
+function Omen:RegisterModuleOptions(name, optionTbl, displayName)
+ if moduleOptions then
+ moduleOptions[name] = optionTbl
+ else
+ self.Options.args[name] = (type(optionTbl) == "function") and optionTbl() or optionTbl
+ end
+ self.optionsFrames[name] = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("Omen", displayName, self.versionstring, name)
+end
+
+function Omen:ShowConfig()
+ -- Open the profiles tab before, so the menu expands
+ InterfaceOptionsFrame_OpenToCategory(self.optionsFrames.Profiles)
+ InterfaceOptionsFrame_OpenToCategory(self.optionsFrames.Omen)
+end
+
+function Omen.GenerateOptions()
+ if Omen.noconfig then assert(false, Omen.noconfig) end
+ if not Omen.Options then
+ Omen.GenerateOptionsInternal()
+ Omen.GenerateOptionsInternal = nil
+ moduleOptions = nil
+ end
+ return Omen.Options
+end
+
+
+-----------------------------------------------------------------------------
+-- Omen config tables
+
+-- Option table for the slash command only
+Omen.OptionsSlash = {
+ type = "group",
+ name = L["Slash Command"],
+ order = -3,
+ args = {
+ intro = {
+ order = 1,
+ type = "description",
+ name = L["OMEN_SLASH_DESC"],
+ cmdHidden = true,
+ },
+ toggle = {
+ type = "execute",
+ name = L["Toggle Omen"],
+ desc = L["Toggle Omen"].." ( /omen toggle )",
+ func = function() Omen:Toggle() end,
+ },
+ center = {
+ type = "execute",
+ name = L["Center Omen"],
+ desc = L["Center Omen"].." ( /omen center )",
+ func = function()
+ Omen.Anchor:ClearAllPoints()
+ Omen.Anchor:SetPoint("CENTER", UIParent, "CENTER")
+ Omen:SetAnchors()
+ end,
+ },
+ config = {
+ type = "execute",
+ name = L["Configure"],
+ desc = L["Open the configuration dialog"].." ( /omen config )",
+ func = function() Omen:ShowConfig() end,
+ guiHidden = true,
+ },
+ show = {
+ type = "execute",
+ name = L["Show Omen"],
+ desc = L["Show Omen"].." ( /omen show )",
+ func = function() Omen:Toggle(true) end,
+ },
+ hide = {
+ type = "execute",
+ name = L["Hide Omen"],
+ desc = L["Hide Omen"].." ( /omen hide )",
+ func = function() Omen:Toggle(false) end,
+ },
+ },
+}
+
+-- This is to provide better error reporting feedback, and stop loading the rest of the file.
+if not AceGUIWidgetLSMlists then
+ Omen.noconfig = 'Cannot find a library instance of "AceGUI-3.0-SharedMediaWidgets". Omen configuration will not be available.'
+ assert(AceGUIWidgetLSMlists, Omen.noconfig)
+end
+
+function Omen.GenerateOptionsInternal()
+ local outlines = {
+ [""] = L["None"],
+ ["OUTLINE"] = L["Outline"],
+ ["THICKOUTLINE"] = L["Thick Outline"],
+ }
+
+ local function GetFuBarMinimapAttachedStatus(info)
+ return Omen:IsFuBarMinimapAttached() or db.FuBar.HideMinimapButton
+ end
+
+-- Option table for the AceGUI config only
+Omen.Options = {
+ type = "group",
+ name = "Omen",
+ get = function(info) return db[ info[#info] ] end,
+ set = function(info, value) db[ info[#info] ] = value end,
+ args = {
+ General = {
+ order = 1,
+ type = "group",
+ name = L["General Settings"],
+ desc = L["General Settings"],
+ args = {
+ intro = {
+ order = 1,
+ type = "description",
+ name = L["OMEN_DESC"],
+ },
+ Alpha = {
+ order = 3,
+ name = L["Alpha"],
+ desc = L["Controls the transparency of the main Omen window."],
+ type = "range",
+ min = 0, max = 1, step = 0.01,
+ isPercent = true,
+ set = function(info, value)
+ db.Alpha = value
+ Omen.Anchor:SetAlpha(value)
+ end,
+ },
+ Scale = {
+ order = 4,
+ name = L["Scale"],
+ desc = L["Controls the scaling of the main Omen window."],
+ type = "range",
+ min = 0.50, max = 1.50, step = 0.01,
+ isPercent = true,
+ set = function(info, value)
+ db.Scale = value
+ Omen:SetAnchors()
+ end,
+ },
+ FrameStrata = {
+ type = "select",
+ order = 5,
+ name = L["Frame Strata"],
+ desc = L["Controls the frame strata of the main Omen window. Default: MEDIUM"],
+ values = { -- A hack to sort them in the menu
+ ["1-BACKGROUND"] = "BACKGROUND",
+ ["2-LOW"] = "LOW",
+ ["3-MEDIUM"] = "MEDIUM",
+ ["4-HIGH"] = "HIGH",
+ ["5-DIALOG"] = "DIALOG",
+ ["6-FULLSCREEN"] = "FULLSCREEN",
+ ["7-FULLSCREEN_DIALOG"] = "FULLSCREEN_DIALOG",
+ ["8-TOOLTIP"] = "TOOLTIP",
+ },
+ set = function(info, value)
+ db.FrameStrata = value
+ Omen.Anchor:SetFrameStrata(strsub(value, 3))
+ end,
+ },
+ ClampToScreen = {
+ type = "toggle",
+ name = L["Clamp To Screen"],
+ desc = L["Controls whether the main Omen window can be dragged offscreen"],
+ order = 6,
+ set = function(info, value)
+ db.ClampToScreen = value
+ Omen.Anchor:SetClampedToScreen(value)
+ end,
+ },
+ Locked = {
+ type = "toggle",
+ name = L["Lock Omen"],
+ desc = L["Locks Omen in place and prevents it from being dragged or resized."],
+ order = 7,
+ set = function(info, value)
+ db.Locked = value
+ Omen:UpdateGrips()
+ end,
+ },
+ UseFocus = {
+ type = "toggle",
+ name = L["Use Focus Target"],
+ desc = L["Tells Omen to additionally check your 'focus' and 'focustarget' before your 'target' and 'targettarget' in that order for threat display."],
+ order = 8,
+ set = function(info, value)
+ Omen:ToggleFocus()
+ end,
+ },
+ TestMode = {
+ type = "toggle",
+ name = L["Test Mode"],
+ desc = L["Tells Omen to enter Test Mode so that you can configure Omen's display much more easily."],
+ order = 9,
+ get = function(info) return testMode end,
+ set = function(info, value)
+ testMode = value
+ Omen:UpdateBars()
+ end,
+ },
+ MinimapIcon = {
+ type = "toggle",
+ name = L["Show minimap button"],
+ desc = L["Show the Omen minimap button"],
+ order = 10,
+ get = function(info) return not db.MinimapIcon.hide end,
+ set = function(info, value)
+ db.MinimapIcon.hide = not value
+ if value then LDBIcon:Show("Omen") else LDBIcon:Hide("Omen") end
+ end,
+ hidden = function() return not LDBIcon or IsAddOnLoaded("Broker2FuBar") or IsAddOnLoaded("FuBar") end,
+ },
+ IgnorePlayerPets = {
+ type = "toggle",
+ name = L["Ignore Player Pets"],
+ desc = L["IGNORE_PLAYER_PETS_DESC"],
+ order = 11,
+ set = function(info, value)
+ db.IgnorePlayerPets = value
+ Omen:UpdateBars()
+ end,
+ },
+ ClickThrough = {
+ type = "toggle",
+ name = L["Click Through"],
+ desc = L["Makes the Omen window non-interactive"],
+ order = 12,
+ set = function(info, value)
+ db.ClickThrough = value
+ Omen:UpdateClickThrough()
+ end,
+ },
+ AutocollapseGroup = {
+ type = "group",
+ name = L["Autocollapse Options"],
+ guiInline = true,
+ order = 21,
+ disabled = function() return not db.Autocollapse end,
+ set = function(info, value)
+ db[ info[#info] ] = value
+ Omen:UpdateVisible()
+ Omen:UpdateBars()
+ end,
+ args = {
+ Autocollapse = {
+ type = "toggle",
+ name = L["Autocollapse"],
+ desc = L["Collapse to show a minimum number of bars"],
+ order = 1,
+ set = function(info, value)
+ db.Autocollapse = value
+ Omen.Anchor:SetHeight(6*db.Bar.Height + 5*db.Bar.Spacing + Omen.Title:GetHeight() + 2*db.Background.BarInset)
+ Omen:SetAnchors()
+ Omen.BarList:Show()
+ Omen:UpdateVisible()
+ Omen:UpdateBars()
+ end,
+ disabled = false,
+ },
+ GrowUp = {
+ order = 2,
+ type = "toggle",
+ name = L["Grow bars upwards"],
+ desc = L["Grow bars upwards"],
+ set = function(info, value)
+ db.GrowUp = value
+ Omen:SetAnchors()
+ end,
+ },
+ CollapseHide = {
+ order = 3,
+ type = "toggle",
+ name = L["Hide Omen on 0 bars"],
+ desc = L["Hide Omen entirely if it collapses to show 0 bars"],
+ set = function(info, value)
+ db.CollapseHide = value
+ if value then manualToggle = false end
+ Omen:UpdateVisible()
+ Omen:UpdateBars()
+ end,
+ },
+ NumBars = {
+ order = 4,
+ name = L["Max bars to show"],
+ desc = L["Max number of bars to show"],
+ type = "range",
+ min = 1, max = 40, step = 1,
+ },
+ },
+ },
+ Background = {
+ type = "group",
+ name = L["Background Options"],
+ guiInline = true,
+ order = 31,
+ get = function(info) return db.Background[ info[#info] ] end,
+ set = function(info, value)
+ db.Background[ info[#info] ] = value
+ Omen:UpdateBackdrop()
+ end,
+ args = {
+ Texture = {
+ type = "select", dialogControl = 'LSM30_Background',
+ order = 1,
+ name = L["Background Texture"],
+ desc = L["Texture to use for the frame's background"],
+ values = AceGUIWidgetLSMlists.background,
+ },
+ BorderTexture = {
+ type = "select", dialogControl = 'LSM30_Border',
+ order = 2,
+ name = L["Border Texture"],
+ desc = L["Texture to use for the frame's border"],
+ values = AceGUIWidgetLSMlists.border,
+ },
+ Color = {
+ type = "color",
+ order = 3,
+ name = L["Background Color"],
+ desc = L["Frame's background color"],
+ hasAlpha = true,
+ get = function(info)
+ local t = db.Background.Color
+ return t.r, t.g, t.b, t.a
+ end,
+ set = function(info, r, g, b, a)
+ local t = db.Background.Color
+ t.r, t.g, t.b, t.a = r, g, b, a
+ Omen:UpdateBackdrop()
+ end,
+ },
+ BorderColor = {
+ type = "color",
+ order = 4,
+ name = L["Border Color"],
+ desc = L["Frame's border color"],
+ hasAlpha = true,
+ get = function(info)
+ local t = db.Background.BorderColor
+ return t.r, t.g, t.b, t.a
+ end,
+ set = function(info, r, g, b, a)
+ local t = db.Background.BorderColor
+ t.r, t.g, t.b, t.a = r, g, b, a
+ Omen:UpdateBackdrop()
+ end,
+ },
+ Tile = {
+ type = "toggle",
+ order = 5,
+ name = L["Tile Background"],
+ desc = L["Tile the background texture"],
+ },
+ TileSize = {
+ type = "range",
+ order = 6,
+ name = L["Background Tile Size"],
+ desc = L["The size used to tile the background texture"],
+ min = 16, max = 256, step = 1,
+ disabled = function() return not db.Background.Tile end,
+ },
+ EdgeSize = {
+ type = "range",
+ order = 7,
+ name = L["Border Thickness"],
+ desc = L["The thickness of the border"],
+ min = 1, max = 16, step = 1,
+ },
+ BarInset = {
+ type = "range",
+ order = 8,
+ name = L["Bar Inset"],
+ desc = L["Sets how far inside the frame the threat bars will display from the 4 borders of the frame"],
+ min = 1, max = 16, step = 1,
+ },
+ },
+ },
+ },
+ },
+ ShowWhen = {
+ order = 2,
+ type = "group",
+ name = L["Show When..."],
+ desc = L["Show Omen when..."],
+ get = function(info) return db.ShowWith[ info[#info] ] end,
+ set = function(info, value)
+ db.ShowWith[ info[#info] ] = value
+ manualToggle = false
+ Omen:UpdateVisible()
+ Omen:UpdateBars()
+ end,
+ args = {
+ intro = {
+ order = 1,
+ type = "description",
+ name = L["This section controls when Omen is automatically shown or hidden."],
+ disabled = false,
+ },
+ UseShowWith = {
+ type = "toggle",
+ order = 2,
+ name = L["Use Auto Show/Hide"],
+ desc = L["Use Auto Show/Hide"],
+ disabled = false,
+ },
+ ShowWithGroup = {
+ type = "group",
+ order = 3,
+ guiInline = true,
+ name = L["Use Auto Show/Hide"],
+ desc = L["Use Auto Show/Hide"],
+ disabled = function(info) return not db.ShowWith.UseShowWith end,
+ args = {
+ intro2 = {
+ order = 10,
+ type = "description",
+ name = L["Show Omen when any of the following are true"],
+ },
+ Alone = {
+ type = "toggle",
+ order = 11,
+ name = L["You are alone"],
+ desc = L["Show Omen when you are alone"],
+ },
+ Party = {
+ type = "toggle",
+ order = 12,
+ name = L["You are in a party"],
+ desc = L["Show Omen when you are in a 5-man party"],
+ },
+ Raid = {
+ type = "toggle",
+ order = 13,
+ name = L["You are in a raid"],
+ desc = L["Show Omen when you are in a raid"],
+ },
+ Pet = {
+ type = "toggle",
+ order = 14,
+ name = L["You have a pet"],
+ desc = L["Show Omen when you have a pet out"],
+ },
+ intro3 = {
+ order = 20,
+ type = "description",
+ name = L["However, hide Omen if any of the following are true (higher priority than the above)."],
+ },
+ HideInPVP = {
+ type = "toggle",
+ order = 21,
+ width = "double",
+ name = L["You are in a battleground"],
+ desc = L["Turning this on will cause Omen to hide whenever you are in a battleground or arena."],
+ },
+ HideWhileResting = {
+ type = "toggle",
+ order = 22,
+ width = "double",
+ name = L["You are resting"],
+ desc = L["Turning this on will cause Omen to hide whenever you are in a city or inn."],
+ },
+ HideWhenOOC = {
+ type = "toggle",
+ order = 23,
+ width = "double",
+ name = L["You are not in combat"],
+ desc = L["Turning this on will cause Omen to hide whenever you are not in combat."],
+ set = function(info, value)
+ db.ShowWith.HideWhenOOC = value
+ manualToggle = false
+ if value then
+ Omen:RegisterEvent("PLAYER_REGEN_DISABLED", "UpdateVisible")
+ Omen:RegisterEvent("PLAYER_REGEN_ENABLED", "UpdateVisible")
+ else
+ Omen:UnregisterEvent("PLAYER_REGEN_DISABLED")
+ Omen:UnregisterEvent("PLAYER_REGEN_ENABLED")
+ end
+ Omen:UpdateVisible()
+ Omen:UpdateBars()
+ end,
+ },
+ intro4 = {
+ order = 30,
+ type = "description",
+ name = L["AUTO_SHOW/HIDE_NOTE"],
+ },
+ },
+ },
+ },
+ },
+ ShowClasses = {
+ order = 3,
+ type = "group",
+ name = L["Show Classes..."],
+ desc = L["Show Classes..."],
+ args = {
+ intro = {
+ order = 1,
+ type = "description",
+ name = L["SHOW_CLASSES_DESC"],
+ },
+ Classes = {
+ type = "multiselect",
+ order = 30,
+ name = L["Show bars for these classes"],
+ values = showClassesOptionTable,
+ get = function(info, k) return db.Bar.Classes[k] end,
+ set = function(info, k, v)
+ db.Bar.Classes[k] = v
+ Omen:UpdateBars()
+ end,
+ },
+ },
+ },
+ TitleBar = {
+ order = 4,
+ type = "group",
+ name = L["Title Bar Settings"],
+ desc = L["Title Bar Settings"],
+ get = function(info) return db.TitleBar[ info[#info] ] end,
+ set = function(info, value)
+ db.TitleBar[ info[#info] ] = value
+ Omen:UpdateTitleBar()
+ Omen:UpdateBars()
+ end,
+ args = {
+ intro = {
+ order = 1,
+ type = "description",
+ name = L["Configure title bar settings."],
+ },
+ ShowTitleBar = {
+ type = "toggle",
+ order = 2,
+ name = L["Show Title Bar"],
+ desc = L["Show the Omen Title Bar"],
+ },
+ Height = {
+ type = "range",
+ order = 5,
+ name = L["Title Bar Height"],
+ desc = L["Height of the title bar. The minimum height allowed is twice the background border thickness."],
+ min = 2, max = 32, step = 1,
+ disabled = function() return not db.TitleBar.ShowTitleBar end,
+ },
+ TitleText = {
+ type = "group",
+ name = L["Title Text Options"],
+ guiInline = true,
+ order = 20,
+ set = function(info, value)
+ db.TitleBar[ info[#info] ] = value
+ Omen:UpdateTitleBar()
+ end,
+ disabled = function() return not db.TitleBar.ShowTitleBar end,
+ args = {
+ Font = {
+ type = "select", dialogControl = 'LSM30_Font',
+ order = 1,
+ name = L["Font"],
+ desc = L["The font that the title text will use"],
+ values = AceGUIWidgetLSMlists.font,
+ },
+ FontOutline = {
+ type = "select",
+ order = 2,
+ name = L["Font Outline"],
+ desc = L["The outline that the title text will use"],
+ values = outlines,
+ },
+ FontColor = {
+ type = "color",
+ order = 3,
+ name = L["Font Color"],
+ desc = L["The color of the title text"],
+ hasAlpha = true,
+ get = function(info)
+ local t = db.TitleBar.FontColor
+ return t.r, t.g, t.b, t.a
+ end,
+ set = function(info, r, g, b, a)
+ local t = db.TitleBar.FontColor
+ t.r, t.g, t.b, t.a = r, g, b, a
+ Omen:UpdateTitleBar()
+ end,
+ },
+ FontSize = {
+ type = "range",
+ order = 4,
+ name = L["Font Size"],
+ desc = L["Control the font size of the title text"],
+ min = 4, max = 30, step = 1,
+ },
+ },
+ },
+ UseSameBG = {
+ type = "toggle",
+ order = 30,
+ width = "double",
+ name = L["Use Same Background"],
+ desc = L["Use the same background settings for the title bar as the main window's background"],
+ set = function(info, value)
+ db.TitleBar.UseSameBG = value
+ Omen:UpdateBackdrop()
+ end,
+ disabled = function() return not db.TitleBar.ShowTitleBar end,
+ },
+ Background = {
+ type = "group",
+ name = L["Title Bar Background Options"],
+ guiInline = true,
+ order = 31,
+ get = function(info) return db.TitleBar[ info[#info] ] end,
+ set = function(info, value)
+ db.TitleBar[ info[#info] ] = value
+ Omen:UpdateBackdrop()
+ end,
+ disabled = function() return not db.TitleBar.ShowTitleBar or db.TitleBar.UseSameBG end,
+ args = {
+ Texture = {
+ type = "select", dialogControl = 'LSM30_Background',
+ order = 1,
+ name = L["Background Texture"],
+ desc = L["Texture to use for the frame's background"],
+ values = AceGUIWidgetLSMlists.background,
+ },
+ BorderTexture = {
+ type = "select", dialogControl = 'LSM30_Border',
+ order = 2,
+ name = L["Border Texture"],
+ desc = L["Texture to use for the frame's border"],
+ values = AceGUIWidgetLSMlists.border,
+ },
+ Color = {
+ type = "color",
+ order = 3,
+ name = L["Background Color"],
+ desc = L["Frame's background color"],
+ hasAlpha = true,
+ get = function(info)
+ local t = db.TitleBar.Color
+ return t.r, t.g, t.b, t.a
+ end,
+ set = function(info, r, g, b, a)
+ local t = db.TitleBar.Color
+ t.r, t.g, t.b, t.a = r, g, b, a
+ Omen:UpdateBackdrop()
+ end,
+ },
+ BorderColor = {
+ type = "color",
+ order = 4,
+ name = L["Border Color"],
+ desc = L["Frame's border color"],
+ hasAlpha = true,
+ get = function(info)
+ local t = db.TitleBar.BorderColor
+ return t.r, t.g, t.b, t.a
+ end,
+ set = function(info, r, g, b, a)
+ local t = db.TitleBar.BorderColor
+ t.r, t.g, t.b, t.a = r, g, b, a
+ Omen:UpdateBackdrop()
+ end,
+ },
+ Tile = {
+ type = "toggle",
+ order = 5,
+ name = L["Tile Background"],
+ desc = L["Tile the background texture"],
+ },
+ TileSize = {
+ type = "range",
+ order = 6,
+ name = L["Background Tile Size"],
+ desc = L["The size used to tile the background texture"],
+ min = 16, max = 256, step = 1,
+ disabled = function() return not db.TitleBar.ShowTitleBar or db.TitleBar.UseSameBG or not db.TitleBar.Tile end,
+ },
+ EdgeSize = {
+ type = "range",
+ order = 7,
+ name = L["Border Thickness"],
+ desc = L["The thickness of the border"],
+ min = 1, max = 16, step = 1,
+ },
+ },
+ },
+ },
+ },
+ Bars = {
+ order = 5,
+ type = "group",
+ name = L["Bar Settings"],
+ desc = L["Bar Settings"],
+ get = function(info) return db.Bar[ info[#info] ] end,
+ set = function(info, value)
+ db.Bar[ info[#info] ] = value
+ Omen:UpdateBars()
+ end,
+ args = {
+ intro = {
+ order = 1,
+ type = "description",
+ name = L["Configure bar settings."],
+ },
+ AnimateBars = {
+ type = "toggle",
+ order = 2,
+ name = L["Animate Bars"],
+ desc = L["Smoothly animate bar changes"],
+ },
+ ShortNumbers = {
+ type = "toggle",
+ order = 3,
+ name = L["Short Numbers"],
+ desc = L["Display large numbers in Ks"],
+ disabled = function() return not db.Bar.ShowValue end
+ },
+ Height = {
+ type = "range",
+ order = 4,
+ name = L["Bar Height"],
+ desc = L["Height of each bar"],
+ min = 5, max = 50, step = 1, bigStep = 1,
+ set = function(info, value)
+ db.Bar.Height = value
+ Omen:ReAnchorBars()
+ Omen:ResizeBars()
+ Omen:UpdateBars()
+ end,
+ },
+ Spacing = {
+ type = "range",
+ order = 5,
+ name = L["Bar Spacing"],
+ desc = L["Spacing between each bar"],
+ min = 0, max = 20, step = 1, bigStep = 1,
+ set = function(info, value)
+ db.Bar.Spacing = value
+ Omen:ReAnchorBars()
+ Omen:UpdateBars()
+ end,
+ },
+ ShowTPS = {
+ type = "toggle",
+ order = 6,
+ name = L["Show TPS"],
+ desc = L["Show threat per second values"],
+ set = function(info, value)
+ db.Bar.ShowTPS = value
+ if db.VGrip1 > db.VGrip2 then
+ db.VGrip1, db.VGrip2 = db.VGrip2, db.VGrip1
+ end
+ movegrip1()
+ movegrip2()
+ Omen:UpdateGrips()
+ local f = Omen.TPSUpdateFrame
+ if f then
+ if value then f:Show() else f:Hide() end
+ end
+ end,
+ },
+ TPSWindow = {
+ type = "range",
+ order = 7,
+ name = L["TPS Window"],
+ desc = L["TPS_WINDOW_DESC"],
+ min = 3, max = 15, step = 0.1,
+ disabled = function() return not db.Bar.ShowTPS end,
+ },
+ ShowValue = {
+ type = "toggle",
+ order = 8,
+ name = L["Show Threat Values"],
+ desc = L["Show Threat Values"],
+ set = function(info, value)
+ db.Bar.ShowValue = value
+ if not value then
+ db.Bar.ShowPercent = true
+ bars[0].Text2:SetText(L["Threat"])
+ elseif db.Bar.ShowPercent then
+ bars[0].Text2:SetText(L["Threat [%]"])
+ end
+ Omen:UpdateBars()
+ end,
+ },
+ ShowPercent = {
+ type = "toggle",
+ order = 9,
+ name = L["Show Threat %"],
+ desc = L["Show Threat %"],
+ set = function(info, value)
+ db.Bar.ShowPercent = value
+ if not value then
+ db.Bar.ShowValue = true
+ bars[0].Text2:SetText(L["Threat"])
+ elseif db.Bar.ShowValue then
+ bars[0].Text2:SetText(L["Threat [%]"])
+ end
+ Omen:UpdateBars()
+ end,
+ },
+ ShowHeadings = {
+ type = "toggle",
+ order = 11,
+ name = L["Show Headings"],
+ desc = L["Show column headings"],
+ set = function(info, value)
+ db.Bar.ShowHeadings = value
+ Omen:ReAnchorBars()
+ Omen:UpdateBars()
+ end,
+ },
+ HeadingBGColor = {
+ type = "color",
+ order = 12,
+ name = L["Heading BG Color"],
+ desc = L["Heading background color"],
+ hasAlpha = true,
+ get = function(info)
+ local t = db.Bar.HeadingBGColor
+ return t.r, t.g, t.b, t.a
+ end,
+ set = function(info, r, g, b, a)
+ local t = db.Bar.HeadingBGColor
+ t.r, t.g, t.b, t.a = r, g, b, a
+ Omen:UpdateBarLabelSettings()
+ Omen:UpdateBars()
+ end,
+ disabled = function() return not db.Bar.ShowHeadings end,
+ },
+ UseMyBarColor = {
+ type = "toggle",
+ order = 13,
+ name = L["Use 'My Bar' color"],
+ desc = L["Use a different colored background for your threat bar in Omen"],
+ },
+ MyBarColor = {
+ type = "color",
+ order = 14,
+ name = L["'My Bar' BG Color"],
+ desc = L["The background color for your threat bar"],
+ hasAlpha = true,
+ get = function(info)
+ local t = db.Bar.MyBarColor
+ return t.r, t.g, t.b, t.a
+ end,
+ set = function(info, r, g, b, a)
+ local t = db.Bar.MyBarColor
+ t.r, t.g, t.b, t.a = r, g, b, a
+ Omen:UpdateBars()
+ end,
+ disabled = function() return not db.Bar.UseMyBarColor end,
+ },
+ UseTankBarColor = {
+ type = "toggle",
+ order = 15,
+ name = L["Use Tank Bar color"],
+ desc = L["Use a different colored background for the tank's threat bar in Omen"],
+ },
+ TankBarColor = {
+ type = "color",
+ order = 16,
+ name = L["Tank Bar Color"],
+ desc = L["The background color for your tank's threat bar"],
+ hasAlpha = true,
+ get = function(info)
+ local t = db.Bar.TankBarColor
+ return t.r, t.g, t.b, t.a
+ end,
+ set = function(info, r, g, b, a)
+ local t = db.Bar.TankBarColor
+ t.r, t.g, t.b, t.a = r, g, b, a
+ Omen:UpdateBars()
+ end,
+ disabled = function() return not db.Bar.UseTankBarColor end,
+ },
+ ShowAggroBar = {
+ type = "toggle",
+ order = 17,
+ name = L["Show Pull Aggro Bar"],
+ desc = L["Show a bar for the amount of threat you will need to reach in order to pull aggro."],
+ },
+ AggroBarColor = {
+ type = "color",
+ order = 18,
+ name = L["Pull Aggro Bar Color"],
+ desc = L["The background color for your Pull Aggro bar"],
+ hasAlpha = true,
+ get = function(info)
+ local t = db.Bar.AggroBarColor
+ return t.r, t.g, t.b, t.a
+ end,
+ set = function(info, r, g, b, a)
+ local t = db.Bar.AggroBarColor
+ t.r, t.g, t.b, t.a = r, g, b, a
+ Omen:UpdateBars()
+ end,
+ disabled = function() return not db.Bar.ShowAggroBar end,
+ },
+ UseClassColors = {
+ type = "toggle",
+ order = 21,
+ name = L["Use Class Colors"],
+ desc = L["Use standard class colors for the background color of threat bars"],
+ },
+ PetBarColor = {
+ type = "color",
+ order = 22,
+ name = L["Pet Bar Color"],
+ desc = L["The background color for pets"],
+ hasAlpha = true,
+ get = function(info)
+ local t = db.Bar.PetBarColor
+ return t.r, t.g, t.b, t.a
+ end,
+ set = function(info, r, g, b, a)
+ local t = db.Bar.PetBarColor
+ t.r, t.g, t.b, t.a = r, g, b, a
+ Omen:UpdateBars()
+ end,
+ disabled = function() return not db.Bar.UseClassColors end,
+ },
+ UseCustomClassColors = {
+ type = "toggle",
+ order = 23,
+ name = L["Use !ClassColors"],
+ desc = L["Use !ClassColors addon for class colors for the background color of threat bars"],
+ set = function(info, v)
+ db.Bar.UseCustomClassColors = v
+ Omen:UpdateRaidClassColors()
+ end,
+ disabled = function() return not db.Bar.UseClassColors or not CUSTOM_CLASS_COLORS end,
+ },
+ FadeBarColor = {
+ type = "color",
+ order = 24,
+ name = L["Fade/MI Bar Color"],
+ desc = L["The background color for players under the effects of Fade and Mirror Image (they will be at negative 4 million threat)"],
+ hasAlpha = true,
+ get = function(info)
+ local t = db.Bar.FadeBarColor
+ return t.r, t.g, t.b, t.a
+ end,
+ set = function(info, r, g, b, a)
+ local t = db.Bar.FadeBarColor
+ t.r, t.g, t.b, t.a = r, g, b, a
+ Omen:UpdateBars()
+ end,
+ },
+ BarColor = {
+ type = "color",
+ order = 25,
+ name = L["Bar BG Color"],
+ desc = L["The background color for all threat bars"],
+ hasAlpha = true,
+ get = function(info)
+ local t = db.Bar.BarColor
+ return t.r, t.g, t.b, t.a
+ end,
+ set = function(info, r, g, b, a)
+ local t = db.Bar.BarColor
+ t.r, t.g, t.b, t.a = r, g, b, a
+ Omen:UpdateBars()
+ end,
+ disabled = function() return db.Bar.UseClassColors end,
+ },
+ AlwaysShowSelf = {
+ type = "toggle",
+ order = 26,
+ name = L["Always Show Self"],
+ desc = L["Always show your threat bar on Omen (ignores class filter settings), showing your bar on the last row if necessary"],
+ },
+ InvertColors = {
+ type = "toggle",
+ order = 27,
+ name = L["Invert Bar/Text Colors"],
+ desc = L["Switch the colors so that the bar background colors and the text colors are swapped."],
+ set = function(info, v)
+ db.Bar.InvertColors = v
+ Omen:UpdateBarLabelSettings()
+ Omen:UpdateBars()
+ end,
+ },
+ Texture = {
+ type = "select", dialogControl = 'LSM30_Statusbar',
+ order = 29,
+ name = L["Bar Texture"],
+ desc = L["The texture that the bar will use"],
+ values = AceGUIWidgetLSMlists.statusbar,
+ set = function(info, v)
+ db.Bar.Texture = v
+ Omen:UpdateBarTextureSettings()
+ end,
+ },
+ BarLabelsGroup = {
+ type = "group",
+ name = L["Bar Label Options"],
+ guiInline = true,
+ order = 30,
+ set = function(info, v)
+ db.Bar[ info[#info] ] = v
+ Omen:UpdateBarLabelSettings()
+ Omen:UpdateBars()
+ end,
+ args = {
+ Font = {
+ type = "select", dialogControl = 'LSM30_Font',
+ order = 1,
+ name = L["Font"],
+ desc = L["The font that the labels will use"],
+ values = AceGUIWidgetLSMlists.font,
+ },
+ FontOutline = {
+ type = "select",
+ order = 2,
+ name = L["Font Outline"],
+ desc = L["The outline that the labels will use"],
+ values = outlines,
+ },
+ FontColor = {
+ type = "color",
+ order = 3,
+ name = L["Font Color"],
+ desc = L["The color of the labels"],
+ hasAlpha = true,
+ get = function(info)
+ local t = db.Bar.FontColor
+ return t.r, t.g, t.b, t.a
+ end,
+ set = function(info, r, g, b, a)
+ local t = db.Bar.FontColor
+ t.r, t.g, t.b, t.a = r, g, b, a
+ Omen:UpdateBarLabelSettings()
+ Omen:UpdateBars()
+ end,
+ },
+ FontSize = {
+ type = "range",
+ order = 4,
+ name = L["Font Size"],
+ desc = L["Control the font size of the labels"],
+ min = 4, max = 30, step = 1,
+ },
+ },
+ },
+ },
+ },
+ Warnings = {
+ order = 6,
+ type = "group",
+ name = L["Warning Settings"],
+ desc = L["Warning Settings"],
+ get = function(info) return db.Warnings[ info[#info] ] end,
+ set = function(info, value)
+ db.Warnings[ info[#info] ] = value
+ end,
+ args = {
+ intro = {
+ order = 1,
+ type = "description",
+ name = L["OMEN_WARNINGS_DESC"],
+ },
+ Sound = {
+ type = "toggle",
+ order = 2,
+ name = L["Enable Sound"],
+ desc = L["Causes Omen to play a chosen sound effect"],
+ },
+ Flash = {
+ type = "toggle",
+ order = 3,
+ name = L["Enable Screen Flash"],
+ desc = L["Causes the entire screen to flash red momentarily"],
+ },
+ Shake = {
+ type = "toggle",
+ order = 4,
+ name = L["Enable Screen Shake"],
+ desc = L["Causes the entire game world to shake momentarily. This option only works if nameplates are turned off."],
+ },
+ Message = {
+ type = "toggle",
+ order = 5,
+ name = L["Enable Warning Message"],
+ desc = L["Print a message to screen when you accumulate too much threat"],
+ },
+ Output = Omen:GetSinkAce3OptionsDataTable(),
+ Threshold = {
+ type = "range",
+ order = 7,
+ name = L["Warning Threshold %"],
+ desc = L["Warning Threshold %"],
+ min = 60, max = 130, step = 1,
+ },
+ SoundFile = {
+ type = "select", dialogControl = 'LSM30_Sound',
+ order = 8,
+ name = L["Sound to play"],
+ desc = L["Sound to play"],
+ values = AceGUIWidgetLSMlists.sound,
+ disabled = function() return not db.Warnings.Sound end,
+ },
+ DisableWhileTanking = {
+ type = "toggle",
+ order = 9,
+ name = L["Disable while tanking"],
+ desc = L["DISABLE_WHILE_TANKING_DESC"],
+ },
+ test = {
+ type = "execute",
+ order = -1,
+ name = L["Test warnings"],
+ desc = L["Test warnings"],
+ func = function()
+ local t = db.Warnings
+ Omen:Warn(t.Sound, t.Flash, t.Shake, t.Message and L["Test warnings"])
+ end,
+ },
+ },
+ },
+ FuBar = {
+ order = -4,
+ type = "group",
+ name = L["FuBar Options"],
+ desc = L["FuBar Options"],
+ hidden = function() return Omen.IsFuBarMinimapAttached == nil end,
+ args = {
+ hideIcon = {
+ type = "toggle",
+ order = 1,
+ name = L["Hide minimap/FuBar icon"],
+ desc = L["Hide minimap/FuBar icon"],
+ get = function(info) return db.FuBar.HideMinimapButton end,
+ set = function(info, v)
+ db.FuBar.HideMinimapButton = v
+ Omen:UpdateFuBarSettings()
+ end,
+ },
+ attachMinimap = {
+ type = "toggle",
+ order = 2,
+ name = L["Attach to minimap"],
+ desc = L["Attach to minimap"],
+ get = function(info) return Omen:IsFuBarMinimapAttached() end,
+ set = function(info, v)
+ Omen:ToggleFuBarMinimapAttached()
+ db.FuBar.AttachMinimap = Omen:IsFuBarMinimapAttached()
+ end,
+ disabled = function() return db.FuBar.HideMinimapButton end,
+ },
+ showIcon = {
+ type = "toggle",
+ order = 3,
+ name = L["Show icon"],
+ desc = L["Show icon"],
+ get = function(info) return Omen:IsFuBarIconShown() end,
+ set = function(info, v) Omen:ToggleFuBarIconShown() end,
+ disabled = GetFuBarMinimapAttachedStatus,
+ },
+ showText = {
+ type = "toggle",
+ order = 4,
+ name = L["Show text"],
+ desc = L["Show text"],
+ get = function(info) return Omen:IsFuBarTextShown() end,
+ set = function(info, v) Omen:ToggleFuBarTextShown() end,
+ disabled = GetFuBarMinimapAttachedStatus,
+ },
+ position = {
+ type = "select",
+ order = 5,
+ name = L["Position"],
+ desc = L["Position"],
+ values = {LEFT = L["Left"], CENTER = L["Center"], RIGHT = L["Right"]},
+ get = function() return Omen:GetPanel() and Omen:GetPanel():GetPluginSide(Omen) end,
+ set = function(info, val)
+ if Omen:GetPanel() and Omen:GetPanel().SetPluginSide then
+ Omen:GetPanel():SetPluginSide(Omen, val)
+ end
+ end,
+ disabled = GetFuBarMinimapAttachedStatus,
+ }
+ }
+ },
+ Help = {
+ type = "group",
+ order = -1,
+ name = L["Help File"],
+ desc = L["A collection of help pages"],
+ childGroups = "select",
+ args = {
+ FAQ1 = {
+ type = "group",
+ order = 1,
+ name = L["FAQ Part 1"],
+ args = {
+ header = {
+ type = "header",
+ name = L["Frequently Asked Questions"],
+ order = 0,
+ },
+ text = {
+ order = 1,
+ type = "description",
+ name = L["GENERAL_FAQ"],
+ },
+ },
+ },
+ FAQ2 = {
+ type = "group",
+ order = 2,
+ name = L["FAQ Part 2"],
+ args = {
+ header = {
+ type = "header",
+ name = L["Frequently Asked Questions"],
+ order = 0,
+ },
+ text = {
+ order = 1,
+ type = "description",
+ name = L["GENERAL_FAQ2"],
+ },
+ },
+ },
+ WARRIOR = {
+ type = "group",
+ name = L["Warrior"],
+ args = {
+ header = {
+ type = "header",
+ name = L["Warrior"],
+ order = 0,
+ },
+ text = {
+ order = 1,
+ type = "description",
+ name = L["WARRIOR_FAQ"],
+ },
+ },
+ },
+ },
+ },
+ },
+}
+Omen.Options.args.Warnings.args.Output.order = 6
+Omen.Options.args.Warnings.args.Output.inline = true
+Omen.Options.args.Warnings.args.Output.disabled = function() return not db.Warnings.Message end
+
+ for k, v in pairs(moduleOptions) do
+ Omen.Options.args[k] = (type(v) == "function") and v() or v
+ end
+
+ -- Add ordering data to the option table generated by AceDBOptions-3.0
+ Omen.Options.args.Profiles.order = -2
+
+ local h = db.Background.EdgeSize * 2
+ if not db.TitleBar.UseSameBG then h = db.TitleBar.EdgeSize * 2 end
+ Omen.Options.args.TitleBar.args.Height.min = h
+end
+
diff --git a/Omen.toc b/Omen.toc
index 33903f6..1d41f58 100644
--- a/Omen.toc
+++ b/Omen.toc
@@ -1,48 +1,52 @@
-## Interface: 30300
-## Version: 3.0.9
-## Title: Omen3
-## Notes: A lightweight, flexible, multi-target threat meter.
-## Notes-ruRU: Лёгкий, гибкий, измеритель угрозы.
-## Notes-frFR: Un "threat meter" léger, flexible et multi-cibles.
-## Notes-zhCN: 一个灵活的,多目标的,低资源占用的威胁值计量器。
-## Notes-zhTW: 一個輕量級、有彈性、可監視多個目標的仇恨統計插件。
-## Notes-koKR: 다중-대상에 대한 위협수준 미터기로 보여줍니다.
-## Author: Xinhuan
-## OptionalDeps: Ace3, LibSharedMedia-3.0, AceGUI-3.0-SharedMediaWidgets, LibSink-2.0, LibDBIcon-1.0, !ClassColors
-## X-eMail: xinhuan @ gmail DOT com
-## X-Donate: PayPal:xinhuan @ gmail DOT com
-## X-Category: Combat
-## X-ConfigMode: true
-## SavedVariables: Omen3DB
-
-#@no-lib-strip@
-Libs\LibStub\LibStub.lua
-Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml
-Libs\AceAddon-3.0\AceAddon-3.0.xml
-Libs\AceGUI-3.0\AceGUI-3.0.xml
-Libs\AceConfig-3.0\AceConfig-3.0.xml
-Libs\AceConsole-3.0\AceConsole-3.0.xml
-Libs\AceDB-3.0\AceDB-3.0.xml
-Libs\AceEvent-3.0\AceEvent-3.0.xml
-Libs\AceLocale-3.0\AceLocale-3.0.xml
-Libs\AceTimer-3.0\AceTimer-3.0.xml
-Libs\AceDBOptions-3.0\AceDBOptions-3.0.xml
-Libs\LibSharedMedia-3.0\lib.xml
-Libs\AceGUI-3.0-SharedMediaWidgets\widget.xml
-Libs\LibSink-2.0\lib.xml
-#@end-no-lib-strip@
-Libs\LibDataBroker-1.1\LibDataBroker-1.1.lua
-#@no-lib-strip@
-Libs\LibDBIcon-1.0\LibDBIcon-1.0.lua
-#@end-no-lib-strip@
-
-Localization\enUS.lua
-Localization\deDE.lua
-Localization\frFR.lua
-Localization\esES.lua
-Localization\koKR.lua
-Localization\zhTW.lua
-Localization\zhCN.lua
-Localization\ruRU.lua
-
-Omen.lua
+## Interface: 30300
+## Version: 3.0.9
+## Title: Omen3
+## Notes: A lightweight, flexible, multi-target threat meter.
+## Notes-ruRU: Лёгкий, гибкий, измеритель угрозы.
+## Notes-frFR: Un "threat meter" léger, flexible et multi-cibles.
+## Notes-zhCN: 一个灵活的,多目标的,低资源占用的威胁值计量器。
+## Notes-zhTW: 一個輕量級、有彈性、可監視多個目標的仇恨統計插件。
+## Notes-koKR: 다중-대상에 대한 위협수준 미터기로 보여줍니다.
+## Author: Xinhuan
+## OptionalDeps: Ace3, LibSharedMedia-3.0, AceGUI-3.0-SharedMediaWidgets, LibSink-2.0, LibDBIcon-1.0, !ClassColors
+## X-eMail: xinhuan @ gmail DOT com
+## X-Donate: PayPal:xinhuan @ gmail DOT com
+## X-Category: Combat
+## X-ConfigMode: true
+## SavedVariables: Omen3DB
+## X-Curse-Packaged-Version: v3.0.9
+## X-Curse-Project-Name: Omen Threat Meter
+## X-Curse-Project-ID: omen-threat-meter
+## X-Curse-Repository-ID: wow/omen-threat-meter/mainline
+
+#@no-lib-strip@
+Libs\LibStub\LibStub.lua
+Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml
+Libs\AceAddon-3.0\AceAddon-3.0.xml
+Libs\AceGUI-3.0\AceGUI-3.0.xml
+Libs\AceConfig-3.0\AceConfig-3.0.xml
+Libs\AceConsole-3.0\AceConsole-3.0.xml
+Libs\AceDB-3.0\AceDB-3.0.xml
+Libs\AceEvent-3.0\AceEvent-3.0.xml
+Libs\AceLocale-3.0\AceLocale-3.0.xml
+Libs\AceTimer-3.0\AceTimer-3.0.xml
+Libs\AceDBOptions-3.0\AceDBOptions-3.0.xml
+Libs\LibSharedMedia-3.0\lib.xml
+Libs\AceGUI-3.0-SharedMediaWidgets\widget.xml
+Libs\LibSink-2.0\lib.xml
+#@end-no-lib-strip@
+Libs\LibDataBroker-1.1\LibDataBroker-1.1.lua
+#@no-lib-strip@
+Libs\LibDBIcon-1.0\LibDBIcon-1.0.lua
+#@end-no-lib-strip@
+
+Localization\enUS.lua
+Localization\deDE.lua
+Localization\frFR.lua
+Localization\esES.lua
+Localization\koKR.lua
+Localization\zhTW.lua
+Localization\zhCN.lua
+Localization\ruRU.lua
+
+Omen.lua
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c3e3f7f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+# Omen
+Omen is a threat meter.
+
+Basically, enemies in WoW decide who to attack by deciding who is the most threatening based on the abilities you use. What Omen does is provide accurate values of your group's relative threat level on individual enemies, so that you can see when you're in danger of pulling aggro (or, if you're next on the snack list if your tank bites it). This info is usually only critical in raids, where only tanks can survive aggro, but it's useful for any multi-player situation.