/
--[[ |
Name: AceAddon-2.0 |
Revision: $Rev: 57245 $ |
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) |
Inspired By: Ace 1.x by Turan (turan@gryphon.com) |
Website: http://www.wowace.com/ |
Documentation: http://www.wowace.com/wiki/AceAddon-2.0 |
SVN: http://svn.wowace.com/wowace/trunk/Ace2/AceAddon-2.0 |
Description: Base for all Ace addons to inherit from. |
Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0, (optional) AceConsole-2.0 |
License: LGPL v2.1 |
]] |
local MAJOR_VERSION = "AceAddon-2.0" |
local MINOR_VERSION = "$Revision: 57245 $" |
-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version |
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end |
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end |
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end |
local function safecall(func,...) |
local success, err = pcall(func,...) |
if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("\n(.-: )in.-\n") or "") .. err) end |
end |
-- Localization |
local STANDBY, TITLE, NOTES, VERSION, AUTHOR, DATE, CATEGORY, EMAIL, CREDITS, WEBSITE, CATEGORIES, ABOUT, LICENSE, PRINT_ADDON_INFO, DONATE, DONATE_DESC, HOWTO_DONATE_WINDOWS, HOWTO_DONATE_MAC |
if GetLocale() == "deDE" then |
STANDBY = "|cffff5050(Standby)|r" -- capitalized |
TITLE = "Titel" |
NOTES = "Anmerkung" |
VERSION = "Version" |
AUTHOR = "Autor" |
DATE = "Datum" |
CATEGORY = "Kategorie" |
EMAIL = "E-Mail" |
WEBSITE = "Webseite" |
CREDITS = "Credits" -- fix |
LICENSE = "License" -- fix |
ABOUT = "Ãber" |
PRINT_ADDON_INFO = "Gibt Addondaten aus" |
DONATE = "Donate" -- fix |
DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix |
HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix |
HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix |
CATEGORIES = { |
["Action Bars"] = "Aktionsleisten", |
["Auction"] = "Auktion", |
["Audio"] = "Audio", |
["Battlegrounds/PvP"] = "Schlachtfeld/PvP", |
["Buffs"] = "Stärkungszauber", |
["Chat/Communication"] = "Chat/Kommunikation", |
["Druid"] = "Druide", |
["Hunter"] = "Jäger", |
["Mage"] = "Magier", |
["Paladin"] = "Paladin", |
["Priest"] = "Priester", |
["Rogue"] = "Schurke", |
["Shaman"] = "Schamane", |
["Warlock"] = "Hexenmeister", |
["Warrior"] = "Krieger", |
["Healer"] = "Heiler", |
["Tank"] = "Tank", |
["Caster"] = "Zauberer", |
["Combat"] = "Kampf", |
["Compilations"] = "Zusammenstellungen", |
["Data Export"] = "Datenexport", |
["Development Tools"] = "Entwicklungs Tools", |
["Guild"] = "Gilde", |
["Frame Modification"] = "Frame Veränderungen", |
["Interface Enhancements"] = "Interface Verbesserungen", |
["Inventory"] = "Inventar", |
["Library"] = "Bibliotheken", |
["Map"] = "Karte", |
["Mail"] = "Post", |
["Miscellaneous"] = "Diverses", |
["Quest"] = "Quest", |
["Raid"] = "Schlachtzug", |
["Tradeskill"] = "Beruf", |
["UnitFrame"] = "Einheiten-Fenster", |
} |
elseif GetLocale() == "frFR" then |
STANDBY = "|cffff5050(attente)|r" |
TITLE = "Titre" |
NOTES = "Notes" |
VERSION = "Version" |
AUTHOR = "Auteur" |
DATE = "Date" |
CATEGORY = "Catégorie" |
EMAIL = "E-mail" |
WEBSITE = "Site web" |
CREDITS = "Credits" -- fix |
LICENSE = "License" -- fix |
ABOUT = "A propos" |
PRINT_ADDON_INFO = "Afficher les informations sur l'addon" |
DONATE = "Donate" -- fix |
DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix |
HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix |
HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix |
CATEGORIES = { |
["Action Bars"] = "Barres d'action", |
["Auction"] = "Hôtel des ventes", |
["Audio"] = "Audio", |
["Battlegrounds/PvP"] = "Champs de bataille/JcJ", |
["Buffs"] = "Buffs", |
["Chat/Communication"] = "Chat/Communication", |
["Druid"] = "Druide", |
["Hunter"] = "Chasseur", |
["Mage"] = "Mage", |
["Paladin"] = "Paladin", |
["Priest"] = "Prêtre", |
["Rogue"] = "Voleur", |
["Shaman"] = "Chaman", |
["Warlock"] = "Démoniste", |
["Warrior"] = "Guerrier", |
["Healer"] = "Soigneur", |
["Tank"] = "Tank", |
["Caster"] = "Casteur", |
["Combat"] = "Combat", |
["Compilations"] = "Compilations", |
["Data Export"] = "Exportation de données", |
["Development Tools"] = "Outils de développement", |
["Guild"] = "Guilde", |
["Frame Modification"] = "Modification des fenêtres", |
["Interface Enhancements"] = "Améliorations de l'interface", |
["Inventory"] = "Inventaire", |
["Library"] = "Bibliothèques", |
["Map"] = "Carte", |
["Mail"] = "Courrier", |
["Miscellaneous"] = "Divers", |
["Quest"] = "Quêtes", |
["Raid"] = "Raid", |
["Tradeskill"] = "Métiers", |
["UnitFrame"] = "Fenêtres d'unité", |
} |
elseif GetLocale() == "koKR" then |
STANDBY = "|cffff5050(ì¬ì©ê°ë¥)|r" |
TITLE = "ì 목" |
NOTES = "ë ¸í¸" |
VERSION = "ë²ì " |
AUTHOR = "ì ìì" |
DATE = "ë ì§" |
CATEGORY = "ë¶ë¥" |
EMAIL = "ì ì ì°í¸" |
WEBSITE = "ì¹ ì¬ì´í¸" |
CREDITS = "ê³µë¡ì" |
LICENSE = "ë¼ì´ì¼ì¤" |
ABOUT = "ì ë³´" |
PRINT_ADDON_INFO = "ì ëì¨ì ëí ì 보를 ì¶ë ¥í©ëë¤." |
DONATE = "기ë¶" |
DONATE_DESC = "ì´ ì ëì¨ì ì ìììê² ê¸°ë¶ë¥¼ í©ëë¤." |
HOWTO_DONATE_WINDOWS = "Ctrl-A를 ëë ¤ ë§í¬ë¥¼ ì íí, Ctrl-Cë¡ ë³µì¬í©ëë¤. Alt-Tab ëë ¤ ê²ìì¼ë¡ ë¶í° ëê°í ì¹ ë¸ë¼ì°ì 를 ì½ëë¤. ë³µì¬ë ë§í¬ë¥¼ 주ì ì°½ì ë¶ì¬ë£ê¸° í©ëë¤." |
HOWTO_DONATE_MAC = "Cmd-A를 ëë ¤ ë§í¬ë¥¼ ì íí, Cmd-Cë¡ ë³µì¬í©ëë¤. Cmd-Tab ëë ¤ ê²ìì¼ë¡ ë¶í° ëê°í ì¹ ë¸ë¼ì°ì 를 ì½ëë¤. ë³µì¬ë ë§í¬ë¥¼ 주ì ì°½ì ë¶ì¬ë£ê¸° í©ëë¤." |
CATEGORIES = { |
["Action Bars"] = "ì¡ì ë°", |
["Auction"] = "경매", |
["Audio"] = "ìí¥", |
["Battlegrounds/PvP"] = "ì ì¥/PvP", |
["Buffs"] = "ë²í", |
["Chat/Communication"] = "ëí/ìì¬ìíµ", |
["Druid"] = "ë루ì´ë", |
["Hunter"] = "ì¬ë¥ê¾¼", |
["Mage"] = "ë§ë²ì¬", |
["Paladin"] = "ì±ê¸°ì¬", |
["Priest"] = "ì¬ì ", |
["Rogue"] = "ëì ", |
["Shaman"] = "주ì ì¬", |
["Warlock"] = "íë§ë²ì¬", |
["Warrior"] = "ì ì¬", |
["Healer"] = "íë¬", |
["Tank"] = "í±ì»¤", |
["Caster"] = "ìºì¤í°", |
["Combat"] = "ì í¬", |
["Compilations"] = "ë³µí©", |
["Data Export"] = "ìë£ ì¶ë ¥", |
["Development Tools"] = "ê°ë° ë구", |
["Guild"] = "길ë", |
["Frame Modification"] = "구조 ë³ê²½", |
["Interface Enhancements"] = "ì¸í°íì´ì¤ ê°í", |
["Inventory"] = "ì¸ë²¤í 리", |
["Library"] = "ë¼ì´ë¸ë¬ë¦¬", |
["Map"] = "ì§ë", |
["Mail"] = "ì°í¸", |
["Miscellaneous"] = "기í", |
["Quest"] = "íì¤í¸", |
["Raid"] = "공격ë", |
["Tradeskill"] = "ì 문기ì ", |
["UnitFrame"] = "ì ë íë ì", |
} |
elseif GetLocale() == "zhTW" then |
STANDBY = "|cffff5050(å¾ å½)|r" |
TITLE = "æ¨é¡" |
NOTES = "註è¨" |
VERSION = "çæ¬" |
AUTHOR = "ä½è " |
DATE = "æ¥æ" |
CATEGORY = "é¡å¥" |
EMAIL = "é»åéµä»¶" |
WEBSITE = "網ç«" |
CREDITS = "ç¹å¥æè¬" |
LICENSE = "çæ¬" |
ABOUT = "éæ¼" |
PRINT_ADDON_INFO = "顯示æ件è³è¨ã" |
DONATE = "æè´" |
DONATE_DESC = "æè´éé¢çµ¦æ件ä½è ã" |
HOWTO_DONATE_WINDOWS = "è«æCtrl-Aé¸æ網ç«é£çµï¼Ctrl-Cè¤è£½ç¶²åï¼Alt-Tabåæå°é»è ¦æ¡é¢ï¼æéç覽å¨ï¼å¨ç¶²ååè²¼ä¸ç¶²åã" |
HOWTO_DONATE_MAC = "è«æCmd-Aé¸æ網ç«é£çµï¼Cmd-Cè¤è£½ç¶²åï¼Cmd-Tabåæå°é»è ¦æ¡é¢ï¼æéç覽å¨ï¼å¨ç¶²ååè²¼ä¸ç¶²åã" |
CATEGORIES = { |
["Action Bars"] = "åä½æ¢", |
["Auction"] = "æè³£", |
["Audio"] = "é³æ", |
["Battlegrounds/PvP"] = "æ°å ´/PvP", |
["Buffs"] = "å¢ç", |
["Chat/Communication"] = "è天/éè¨", |
["Druid"] = "å¾·é¯ä¼", |
["Hunter"] = "çµäºº", |
["Mage"] = "æ³å¸«", |
["Paladin"] = "èé¨å£«", |
["Priest"] = "ç§å¸«", |
["Rogue"] = "çè³", |
["Shaman"] = "è©æ»¿", |
["Warlock"] = "è¡å£«", |
["Warrior"] = "æ°å£«", |
["Healer"] = "æ²»çè ", |
["Tank"] = "å¦å ", |
["Caster"] = "æ½æ³è ", |
["Combat"] = "æ°é¬¥", |
["Compilations"] = "æ´å", |
["Data Export"] = "è³æå¯åº", |
["Development Tools"] = "éç¼å·¥å ·", |
["Guild"] = "å ¬æ", |
["Frame Modification"] = "æ¡æ¶ä¿®æ¹", |
["Interface Enhancements"] = "ä»é¢å¢å¼·", |
["Inventory"] = "庫å", |
["Library"] = "ç¨å¼åº«", |
["Map"] = "å°å", |
["Mail"] = "éµä»¶", |
["Miscellaneous"] = "éé ", |
["Quest"] = "ä»»å", |
["Raid"] = "åé", |
["Tradeskill"] = "交ææè½", |
["UnitFrame"] = "å®ä½æ¡æ¶", |
} |
elseif GetLocale() == "zhCN" then |
STANDBY = "|cffff5050(ææ)|r" |
TITLE = "æ é¢" |
NOTES = "é注" |
VERSION = "çæ¬" |
AUTHOR = "ä½è " |
DATE = "æ¥æ" |
CATEGORY = "åç±»" |
EMAIL = "çµåé®ä»¶" |
WEBSITE = "ç½ç«" |
CREDITS = "Credits" -- fix |
LICENSE = "License" -- fix |
ABOUT = "å ³äº" |
PRINT_ADDON_INFO = "å°ååºæ件信æ¯" |
DONATE = "Donate" -- fix |
DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix |
HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix |
HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix |
CATEGORIES = { |
["Action Bars"] = "å¨ä½æ¡", |
["Auction"] = "æå", |
["Audio"] = "é³é¢", |
["Battlegrounds/PvP"] = "æåº/PvP", |
["Buffs"] = "å¢çéæ³", |
["Chat/Communication"] = "è天/交æµ", |
["Druid"] = "å¾·é²ä¼", |
["Hunter"] = "ç人", |
["Mage"] = "æ³å¸", |
["Paladin"] = "å£éªå£«", |
["Priest"] = "ç§å¸", |
["Rogue"] = "çè´¼", |
["Shaman"] = "è¨æ»¡ç¥å¸", |
["Warlock"] = "æ¯å£«", |
["Warrior"] = "æ士", |
["Healer"] = "Healer", |
["Tank"] = "Tank", |
["Caster"] = "Caster", |
["Combat"] = "ææ", |
["Compilations"] = "ç¼è¯", |
["Data Export"] = "æ°æ®å¯¼åº", |
["Development Tools"] = "å¼åå·¥å ·", |
["Guild"] = "å ¬ä¼", |
["Frame Modification"] = "æ¡æ¶ä¿®æ¹", |
["Interface Enhancements"] = "çé¢å¢å¼º", |
["Inventory"] = "èå ", |
["Library"] = "åº", |
["Map"] = "å°å¾", |
["Mail"] = "é®ä»¶", |
["Miscellaneous"] = "æ项", |
["Quest"] = "ä»»å¡", |
["Raid"] = "å¢é", |
["Tradeskill"] = "åä¸æè½", |
["UnitFrame"] = "头åæ¡æ¶", |
} |
elseif GetLocale() == "esES" then |
STANDBY = "|cffff5050(espera)|r" |
TITLE = "TÃtulo" |
NOTES = "Notas" |
VERSION = "Versión" |
AUTHOR = "Autor" |
DATE = "Fecha" |
CATEGORY = "CategorÃa" |
EMAIL = "E-mail" |
WEBSITE = "Web" |
CREDITS = "Créditos" |
LICENSE = "License" -- fix |
ABOUT = "Acerca de" |
PRINT_ADDON_INFO = "Muestra información acerca del accesorio." |
DONATE = "Donate" -- fix |
DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix |
HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix |
HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix |
CATEGORIES = { |
["Action Bars"] = "Barras de Acción", |
["Auction"] = "Subasta", |
["Audio"] = "Audio", |
["Battlegrounds/PvP"] = "Campos de Batalla/JcJ", |
["Buffs"] = "Buffs", |
["Chat/Communication"] = "Chat/Comunicación", |
["Druid"] = "Druida", |
["Hunter"] = "Cazador", |
["Mage"] = "Mago", |
["Paladin"] = "PaladÃn", |
["Priest"] = "Sacerdote", |
["Rogue"] = "PÃcaro", |
["Shaman"] = "Chamán", |
["Warlock"] = "Brujo", |
["Warrior"] = "Guerrero", |
["Healer"] = "Sanador", |
["Tank"] = "Tanque", |
["Caster"] = "Conjurador", |
["Combat"] = "Combate", |
["Compilations"] = "Compilaciones", |
["Data Export"] = "Exportar Datos", |
["Development Tools"] = "Herramientas de Desarrollo", |
["Guild"] = "Hermandad", |
["Frame Modification"] = "Modificación de Marcos", |
["Interface Enhancements"] = "Mejoras de la Interfaz", |
["Inventory"] = "Inventario", |
["Library"] = "Biblioteca", |
["Map"] = "Mapa", |
["Mail"] = "Correo", |
["Miscellaneous"] = "Misceláneo", |
["Quest"] = "Misión", |
["Raid"] = "Banda", |
["Tradeskill"] = "Habilidad de Comercio", |
["UnitFrame"] = "Marco de Unidades", |
} |
else -- enUS |
STANDBY = "|cffff5050(standby)|r" |
TITLE = "Title" |
NOTES = "Notes" |
VERSION = "Version" |
AUTHOR = "Author" |
DATE = "Date" |
CATEGORY = "Category" |
EMAIL = "E-mail" |
WEBSITE = "Website" |
CREDITS = "Credits" |
LICENSE = "License" |
ABOUT = "About" |
PRINT_ADDON_INFO = "Show information about the addon." |
DONATE = "Donate" |
DONATE_DESC = "Give a much-needed donation to the author of this addon." |
HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." |
HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." |
CATEGORIES = { |
["Action Bars"] = "Action Bars", |
["Auction"] = "Auction", |
["Audio"] = "Audio", |
["Battlegrounds/PvP"] = "Battlegrounds/PvP", |
["Buffs"] = "Buffs", |
["Chat/Communication"] = "Chat/Communication", |
["Druid"] = "Druid", |
["Hunter"] = "Hunter", |
["Mage"] = "Mage", |
["Paladin"] = "Paladin", |
["Priest"] = "Priest", |
["Rogue"] = "Rogue", |
["Shaman"] = "Shaman", |
["Warlock"] = "Warlock", |
["Warrior"] = "Warrior", |
["Healer"] = "Healer", |
["Tank"] = "Tank", |
["Caster"] = "Caster", |
["Combat"] = "Combat", |
["Compilations"] = "Compilations", |
["Data Export"] = "Data Export", |
["Development Tools"] = "Development Tools", |
["Guild"] = "Guild", |
["Frame Modification"] = "Frame Modification", |
["Interface Enhancements"] = "Interface Enhancements", |
["Inventory"] = "Inventory", |
["Library"] = "Library", |
["Map"] = "Map", |
["Mail"] = "Mail", |
["Miscellaneous"] = "Miscellaneous", |
["Quest"] = "Quest", |
["Raid"] = "Raid", |
["Tradeskill"] = "Tradeskill", |
["UnitFrame"] = "UnitFrame", |
} |
end |
setmetatable(CATEGORIES, { __index = function(self, key) -- case-insensitive |
local lowerKey = key:lower() |
for k,v in pairs(CATEGORIES) do |
if k:lower() == lowerKey then |
self[lowerKey] = v |
return v |
end |
end |
end }) |
-- Create the library object |
local AceOO = AceLibrary("AceOO-2.0") |
local AceAddon = AceOO.Class() |
local AceEvent |
local AceConsole |
local AceModuleCore |
function AceAddon:GetLocalizedCategory(name) |
self:argCheck(name, 2, "string") |
return CATEGORIES[name] or UNKNOWN |
end |
function AceAddon:ToString() |
return "AceAddon" |
end |
local function print(text) |
DEFAULT_CHAT_FRAME:AddMessage(text) |
end |
function AceAddon:ADDON_LOADED(name) |
local unregister = true |
local initAddon = {} |
while #self.nextAddon > 0 do |
local addon = table.remove(self.nextAddon, 1) |
if addon.possibleNames[name] then |
table.insert(initAddon, addon) |
else |
unregister = nil |
table.insert(self.skipAddon, addon) |
end |
end |
self.nextAddon, self.skipAddon = self.skipAddon, self.nextAddon |
if unregister then |
AceAddon:UnregisterEvent("ADDON_LOADED") |
end |
while #initAddon > 0 do |
local addon = table.remove(initAddon, 1) |
table.insert(self.addons, addon) |
if not self.addons[name] then |
self.addons[name] = addon |
end |
addon.possibleNames = nil |
self:InitializeAddon(addon, name) |
end |
end |
local function RegisterOnEnable(self) |
if DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage then -- HACK |
AceAddon.playerLoginFired = true |
end |
if AceAddon.playerLoginFired then |
AceAddon.addonsStarted[self] = true |
if (type(self.IsActive) ~= "function" or self:IsActive()) and (not AceModuleCore or not AceModuleCore:IsModule(self) or AceModuleCore:IsModuleActive(self)) then |
AceAddon:ManualEnable(self) |
end |
else |
if not AceAddon.addonsToOnEnable then |
AceAddon.addonsToOnEnable = {} |
end |
table.insert(AceAddon.addonsToOnEnable, self) |
end |
end |
function AceAddon:InitializeAddon(addon, name) |
if addon.name == nil then |
addon.name = name |
end |
if GetAddOnMetadata then |
-- TOC checks |
if addon.title == nil then |
addon.title = GetAddOnMetadata(name, "Title") |
end |
if type(addon.title) == "string" then |
local num = addon.title:find(" |cff7fff7f %-Ace2%-|r$") |
if num then |
addon.title = addon.title:sub(1, num - 1) |
end |
addon.title = addon.title:trim() |
end |
if addon.notes == nil then |
addon.notes = GetAddOnMetadata(name, "Notes") |
end |
if type(addon.notes) == "string" then |
addon.notes = addon.notes:trim() |
end |
if addon.version == nil then |
addon.version = GetAddOnMetadata(name, "Version") |
end |
if type(addon.version) == "string" then |
if addon.version:find("%$Revision: (%d+) %$") then |
addon.version = addon.version:gsub("%$Revision: (%d+) %$", "%1") |
elseif addon.version:find("%$Rev: (%d+) %$") then |
addon.version = addon.version:gsub("%$Rev: (%d+) %$", "%1") |
elseif addon.version:find("%$LastChangedRevision: (%d+) %$") then |
addon.version = addon.version:gsub("%$LastChangedRevision: (%d+) %$", "%1") |
end |
addon.version = addon.version:trim() |
end |
if addon.author == nil then |
addon.author = GetAddOnMetadata(name, "Author") |
end |
if type(addon.author) == "string" then |
addon.author = addon.author:trim() |
end |
if addon.credits == nil then |
addon.credits = GetAddOnMetadata(name, "X-Credits") |
end |
if type(addon.credits) == "string" then |
addon.credits = addon.credits:trim() |
end |
if addon.donate == nil then |
addon.donate = GetAddOnMetadata(name, "X-Donate") |
end |
if type(addon.donate) == "string" then |
addon.donate = addon.donate:trim() |
end |
if addon.date == nil then |
addon.date = GetAddOnMetadata(name, "X-Date") or GetAddOnMetadata(name, "X-ReleaseDate") |
end |
if type(addon.date) == "string" then |
if addon.date:find("%$Date: (.-) %$") then |
addon.date = addon.date:gsub("%$Date: (.-) %$", "%1") |
elseif addon.date:find("%$LastChangedDate: (.-) %$") then |
addon.date = addon.date:gsub("%$LastChangedDate: (.-) %$", "%1") |
end |
addon.date = addon.date:trim() |
end |
if addon.category == nil then |
addon.category = GetAddOnMetadata(name, "X-Category") |
end |
if type(addon.category) == "string" then |
addon.category = addon.category:trim() |
end |
if addon.email == nil then |
addon.email = GetAddOnMetadata(name, "X-eMail") or GetAddOnMetadata(name, "X-Email") |
end |
if type(addon.email) == "string" then |
addon.email = addon.email:trim() |
end |
if addon.license == nil then |
addon.license = GetAddOnMetadata(name, "X-License") |
end |
if type(addon.license) == "string" then |
addon.license = addon.license:trim() |
end |
if addon.website == nil then |
addon.website = GetAddOnMetadata(name, "X-Website") |
end |
if type(addon.website) == "string" then |
addon.website = addon.website:trim() |
end |
end |
local current = addon.class |
while true do |
if current == AceOO.Class or not current then |
break |
end |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedInitialize) == "function" then |
mixin:OnEmbedInitialize(addon, name) |
end |
end |
end |
current = current.super |
end |
local n = AceAddon.addonsToOnEnable and #AceAddon.addonsToOnEnable or 0 |
if type(addon.OnInitialize) == "function" then |
safecall(addon.OnInitialize, addon, name) |
end |
if AceEvent then |
AceEvent:TriggerEvent("Ace2_AddonInitialized", addon) |
end |
RegisterOnEnable(addon) |
local n2 = AceAddon.addonsToOnEnable and #AceAddon.addonsToOnEnable or 0 |
if n2 - n > 1 then |
local mine = table.remove(AceAddon.addonsToOnEnable) |
table.insert(AceAddon.addonsToOnEnable, n+1, mine) |
end |
end |
local aboutFrame |
local function createAboutFrame() |
aboutFrame = CreateFrame("Frame", "AceAddon20AboutFrame", UIParent, "DialogBoxFrame") |
aboutFrame:SetWidth(500) |
aboutFrame:SetHeight(400) |
aboutFrame:SetPoint("CENTER") |
aboutFrame:SetBackdrop({ |
bgFile = [[Interface\DialogFrame\UI-DialogBox-Background]], |
edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]], |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 5, right = 5, top = 5, bottom = 5 } |
}) |
aboutFrame:SetBackdropColor(0,0,0,1) |
local donateButton = CreateFrame("Button", "AceAddon20AboutFrameDonateButton", aboutFrame, "UIPanelButtonTemplate2") |
aboutFrame.donateButton = donateButton |
donateButton:SetPoint("BOTTOMRIGHT", -20, 20) |
_G.AceAddon20AboutFrameDonateButtonText:SetText(DONATE) |
donateButton:SetWidth(_G.AceAddon20AboutFrameDonateButtonText:GetWidth()+20) |
donateButton:SetScript("OnClick", function() |
aboutFrame.currentAddon:OpenDonationFrame() |
end) |
local text = aboutFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge") |
aboutFrame.title = text |
text:SetPoint("TOP", 0, -5) |
aboutFrame:Hide() |
aboutFrame.lefts = {} |
aboutFrame.rights = {} |
aboutFrame.textLefts = {} |
aboutFrame.textRights = {} |
function aboutFrame:Clear() |
self.title:SetText("") |
for i = 1, #self.lefts do |
self.lefts[i] = nil |
self.rights[i] = nil |
end |
end |
function aboutFrame:AddLine(left, right) |
aboutFrame.lefts[#aboutFrame.lefts+1] = left |
aboutFrame.rights[#aboutFrame.rights+1] = right |
end |
local aboutFrame_Show = aboutFrame.Show |
function aboutFrame:Show(...) |
local maxLeftWidth = 0 |
local maxRightWidth = 0 |
local textHeight = 0 |
for i = 1, #self.lefts do |
if not self.textLefts[i] then |
local left = aboutFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") |
self.textLefts[i] = left |
local right = aboutFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlight") |
self.textRights[i] = right |
if i == 1 then |
left:SetPoint("TOPRIGHT", aboutFrame, "TOPLEFT", 75, -35) |
else |
left:SetPoint("TOPRIGHT", self.textLefts[i-1], "BOTTOMRIGHT", 0, -5) |
end |
right:SetPoint("LEFT", left, "RIGHT", 5, 0) |
end |
self.textLefts[i]:SetText(self.lefts[i] .. ":") |
self.textRights[i]:SetText(self.rights[i]) |
local leftWidth = self.textLefts[i]:GetWidth() |
local rightWidth = self.textRights[i]:GetWidth() |
textHeight = self.textLefts[i]:GetHeight() |
if maxLeftWidth < leftWidth then |
maxLeftWidth = leftWidth |
end |
if maxRightWidth < rightWidth then |
maxRightWidth = rightWidth |
end |
end |
for i = #self.lefts+1, #self.textLefts do |
self.textLefts[i]:SetText('') |
self.textRights[i]:SetText('') |
end |
aboutFrame:SetWidth(75 + maxRightWidth + 20) |
aboutFrame:SetHeight(#self.lefts * (textHeight + 5) + 100) |
aboutFrame_Show(self, ...) |
end |
aboutFrame:Hide() |
createAboutFrame = nil |
end |
local donateFrame |
local function unobfuscateEmail(email) |
return email:gsub(" AT ", "@"):gsub(" DOT ", ".") |
end |
local function isGoodVariable(var) |
return type(var) == "string" or type(var) == "number" |
end |
function AceAddon.prototype:PrintAddonInfo() |
if createAboutFrame then |
createAboutFrame() |
end |
aboutFrame:Clear() |
local x |
if isGoodVariable(self.title) then |
x = tostring(self.title) |
elseif isGoodVariable(self.name) then |
x = tostring(self.name) |
else |
x = "<" .. tostring(self.class) .. " instance>" |
end |
if type(self.IsActive) == "function" then |
if not self:IsActive() then |
x = x .. " " .. STANDBY |
end |
end |
aboutFrame.title:SetText(x) |
if isGoodVariable(self.version) then |
aboutFrame:AddLine(VERSION, tostring(self.version)) |
end |
if isGoodVariable(self.notes) then |
aboutFrame:AddLine(NOTES, tostring(self.notes)) |
end |
if isGoodVariable(self.author) then |
aboutFrame:AddLine(AUTHOR, tostring(self.author)) |
end |
if isGoodVariable(self.credits) then |
aboutFrame:AddLine(CREDITS, tostring(self.credits)) |
end |
if isGoodVariable(self.date) then |
aboutFrame:AddLine(DATE, tostring(self.date)) |
end |
if isGoodVariable(self.category) then |
local category = CATEGORIES[self.category] |
aboutFrame:AddLine(CATEGORY, category or tostring(self.category)) |
end |
if isGoodVariable(self.email) then |
aboutFrame:AddLine(EMAIL, unobfuscateEmail(tostring(self.email))) |
end |
if isGoodVariable(self.website) then |
aboutFrame:AddLine(WEBSITE, tostring(self.website)) |
end |
if isGoodVariable(self.license) then |
aboutFrame:AddLine(LICENSE, tostring(self.license)) |
end |
if donateFrame and donateFrame:IsShown() then |
donateFrame:Hide() |
end |
aboutFrame.currentAddon = self |
aboutFrame:Show() |
if self.donate then |
aboutFrame.donateButton:Show() |
else |
aboutFrame.donateButton:Hide() |
end |
end |
local function createDonateFrame() |
donateFrame = CreateFrame("Frame", "AceAddon20Frame", UIParent, "DialogBoxFrame") |
donateFrame:SetWidth(500) |
donateFrame:SetHeight(200) |
donateFrame:SetPoint("CENTER") |
donateFrame:SetBackdrop({ |
bgFile = [[Interface\DialogFrame\UI-DialogBox-Background]], |
edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]], |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 5, right = 5, top = 5, bottom = 5 } |
}) |
donateFrame:SetBackdropColor(0,0,0,1) |
local text = donateFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge") |
text:SetPoint("TOP", 0, -5) |
text:SetText(DONATE) |
local howto = donateFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlight") |
howto:SetPoint("TOP", text, "BOTTOM", 0, -5) |
howto:SetPoint("LEFT", 16, 0) |
howto:SetPoint("RIGHT", -16, 0) |
if not IsMacClient() then |
-- Windows or Linux |
howto:SetText(HOWTO_DONATE_WINDOWS) |
else |
howto:SetText(HOWTO_DONATE_MAC) |
end |
local scrollFrame = CreateFrame("ScrollFrame", "AceAddon20FrameScrollFrame", donateFrame, "UIPanelScrollFrameTemplate") |
scrollFrame:SetToplevel(true) |
scrollFrame:SetPoint("TOP", -10, -76) |
scrollFrame:SetWidth(455) |
scrollFrame:SetHeight(70) |
howto:SetPoint("BOTTOM", scrollFrame, "TOP") |
local editBox = CreateFrame("EditBox", nil, scrollFrame) |
donateFrame.editBox = editBox |
scrollFrame:SetScrollChild(editBox) |
editBox:SetFontObject(ChatFontNormal) |
editBox:SetMultiLine(true) |
editBox:SetMaxLetters(99999) |
editBox:SetWidth(450) |
editBox:SetHeight(54) |
editBox:SetPoint("BOTTOM", 5, 0) |
editBox:SetJustifyH("LEFT") |
editBox:SetJustifyV("TOP") |
editBox:SetAutoFocus(false) |
editBox:SetScript("OnTextChanged", function(this) |
if this:GetText() ~= this.text then |
this:SetText(this.text) |
end |
end) |
editBox:SetScript("OnEscapePressed", function(this) |
this:ClearFocus() |
end) |
createDonateFrame = nil |
end |
local function fix(char) |
return ("%%%02x"):format(char:byte()) |
end |
local function urlencode(text) |
return text:gsub("[^0-9A-Za-z]", fix) |
end |
function AceAddon.prototype:OpenDonationFrame() |
if createDonateFrame then |
createDonateFrame() |
end |
local donate = self.donate |
if type(donate) ~= "string" then |
donate = "Wowace" |
end |
local style, data = (":"):split(donate, 2) |
style = style:lower() |
if style ~= "website" and style ~= "paypal" then |
style = "wowace" |
end |
if style == "wowace" then |
donateFrame.editBox.text = "http://www.wowace.com/wiki/Donations" |
elseif style == "website" then |
donateFrame.editBox.text = data |
else -- PayPal |
local text = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=" .. urlencode(unobfuscateEmail(data)) |
local name |
if type(self.title) == "string" then |
name = self.title |
elseif type(self.name) == "string" then |
name = self.name |
end |
if name then |
name = name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "") |
text = text .. "&item_name=" .. urlencode(name) |
end |
donateFrame.editBox.text = text |
end |
donateFrame.editBox:SetText(donateFrame.editBox.text) |
if aboutFrame and aboutFrame:IsShown() then |
aboutFrame:Hide() |
end |
donateFrame:Show() |
donateFrame.editBox:SetFocus() |
end |
local options |
function AceAddon:GetAceOptionsDataTable(target) |
return { |
about = { |
name = ABOUT, |
desc = PRINT_ADDON_INFO, |
type = "execute", |
func = "PrintAddonInfo", |
order = -1, |
}, |
donate = { |
name = DONATE, |
desc = DONATE_DESC, |
type = "execute", |
func = "OpenDonationFrame", |
order = -1, |
hidden = function() |
return not target.donate |
end |
} |
} |
end |
function AceAddon:PLAYER_LOGIN() |
self.playerLoginFired = true |
if self.addonsToOnEnable then |
while #self.addonsToOnEnable > 0 do |
local addon = table.remove(self.addonsToOnEnable, 1) |
self.addonsStarted[addon] = true |
if (type(addon.IsActive) ~= "function" or addon:IsActive()) and (not AceModuleCore or not AceModuleCore:IsModule(addon) or AceModuleCore:IsModuleActive(addon)) then |
AceAddon:ManualEnable(addon) |
end |
end |
self.addonsToOnEnable = nil |
end |
end |
function AceAddon.prototype:Inject(t) |
AceAddon:argCheck(t, 2, "table") |
for k,v in pairs(t) do |
self[k] = v |
end |
end |
function AceAddon.prototype:init() |
if not AceEvent then |
error(MAJOR_VERSION .. " requires AceEvent-2.0", 4) |
end |
AceAddon.super.prototype.init(self) |
self.super = self.class.prototype |
AceAddon:RegisterEvent("ADDON_LOADED", "ADDON_LOADED") |
local names = {} |
for i = 1, GetNumAddOns() do |
if IsAddOnLoaded(i) then names[GetAddOnInfo(i)] = true end |
end |
self.possibleNames = names |
table.insert(AceAddon.nextAddon, self) |
end |
function AceAddon.prototype:ToString() |
local x |
if type(self.title) == "string" then |
x = self.title |
elseif type(self.name) == "string" then |
x = self.name |
else |
x = "<" .. tostring(self.class) .. " instance>" |
end |
if (type(self.IsActive) == "function" and not self:IsActive()) or (AceModuleCore and AceModuleCore:IsModule(addon) and AceModuleCore:IsModuleActive(addon)) then |
x = x .. " " .. STANDBY |
end |
return x |
end |
AceAddon.new = function(self, ...) |
local class = AceAddon:pcall(AceOO.Classpool, self, ...) |
return class:new() |
end |
function AceAddon:ManualEnable(addon) |
AceAddon:argCheck(addon, 2, "table") |
local first = nil |
if AceOO.inherits(addon, "AceAddon-2.0") then |
if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[addon] then |
first = true |
AceAddon.addonsEnabled[addon] = true |
end |
end |
local current = addon.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedEnable) == "function" then |
safecall(mixin.OnEmbedEnable, mixin, addon, first) |
end |
end |
end |
current = current.super |
end |
if type(addon.OnEnable) == "function" then |
safecall(addon.OnEnable, addon, first) |
end |
if AceEvent then |
AceEvent:TriggerEvent("Ace2_AddonEnabled", addon, first) |
end |
end |
function AceAddon:ManualDisable(addon) |
AceAddon:argCheck(addon, 2, "table") |
local current = addon.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedDisable) == "function" then |
safecall(mixin.OnEmbedDisable, mixin, addon) |
end |
end |
end |
current = current.super |
end |
if type(module.OnDisable) == "function" then |
safecall(module.OnDisable, addon) |
end |
if AceEvent then |
AceEvent:TriggerEvent("Ace2_AddonDisabled", addon) |
end |
end |
local function external(self, major, instance) |
if major == "AceEvent-2.0" then |
AceEvent = instance |
AceEvent:embed(self) |
self:RegisterEvent("PLAYER_LOGIN", "PLAYER_LOGIN", true) |
elseif major == "AceConsole-2.0" then |
AceConsole = instance |
local slashCommands = { "/ace2" } |
local _,_,_,enabled,loadable = GetAddOnInfo("Ace") |
if not enabled or not loadable then |
table.insert(slashCommands, "/ace") |
end |
local function listAddon(addon, depth) |
if not depth then |
depth = 0 |
end |
local s = (" "):rep(depth) .. " - " .. tostring(addon) |
if rawget(addon, 'version') then |
s = s .. " - |cffffff7f" .. tostring(addon.version) .. "|r" |
end |
if rawget(addon, 'slashCommand') then |
s = s .. " |cffffff7f(" .. tostring(addon.slashCommand) .. ")|r" |
end |
print(s) |
if type(rawget(addon, 'modules')) == "table" then |
local i = 0 |
for k,v in pairs(addon.modules) do |
i = i + 1 |
if i == 6 then |
print((" "):rep(depth + 1) .. " - more...") |
break |
else |
listAddon(v, depth + 1) |
end |
end |
end |
end |
local function listNormalAddon(i) |
local name,_,_,enabled,loadable = GetAddOnInfo(i) |
if not loadable then |
enabled = false |
end |
if self.addons[name] then |
listAddon(self.addons[name]) |
else |
local s = " - " .. tostring(GetAddOnMetadata(i, "Title") or name) |
local version = GetAddOnMetadata(i, "Version") |
if version then |
if version:find("%$Revision: (%d+) %$") then |
version = version:gsub("%$Revision: (%d+) %$", "%1") |
elseif version:find("%$Rev: (%d+) %$") then |
version = version:gsub("%$Rev: (%d+) %$", "%1") |
elseif version:find("%$LastChangedRevision: (%d+) %$") then |
version = version:gsub("%$LastChangedRevision: (%d+) %$", "%1") |
end |
s = s .. " - |cffffff7f" .. version .. "|r" |
end |
if not enabled then |
s = s .. " |cffff0000(disabled)|r" |
end |
if IsAddOnLoadOnDemand(i) then |
s = s .. " |cff00ff00[LoD]|r" |
end |
print(s) |
end |
end |
local function mySort(alpha, bravo) |
return tostring(alpha) < tostring(bravo) |
end |
AceConsole.RegisterChatCommand(self, slashCommands, { |
desc = "AddOn development framework", |
name = "Ace2", |
type = "group", |
args = { |
about = { |
desc = "Get information about Ace2", |
name = "About", |
type = "execute", |
func = function() |
print("|cffffff7fAce2|r - |cffffff7f2.0." .. MINOR_VERSION:gsub("%$Revision: (%d+) %$", "%1") .. "|r - AddOn development framework") |
print(" - |cffffff7f" .. AUTHOR .. ":|r Ace Development Team") |
print(" - |cffffff7f" .. WEBSITE .. ":|r http://www.wowace.com/") |
end |
}, |
list = { |
desc = "List addons", |
name = "List", |
type = "group", |
args = { |
ace2 = { |
desc = "List addons using Ace2", |
name = "Ace2", |
type = "execute", |
func = function() |
print("|cffffff7fAddon list:|r") |
table.sort(self.addons, mySort) |
for _,v in ipairs(self.addons) do |
listAddon(v) |
end |
end |
}, |
all = { |
desc = "List all addons", |
name = "All", |
type = "execute", |
func = function() |
print("|cffffff7fAddon list:|r") |
local count = GetNumAddOns() |
for i = 1, count do |
listNormalAddon(i) |
end |
end |
}, |
enabled = { |
desc = "List all enabled addons", |
name = "Enabled", |
type = "execute", |
func = function() |
print("|cffffff7fAddon list:|r") |
local count = GetNumAddOns() |
for i = 1, count do |
local _,_,_,enabled,loadable = GetAddOnInfo(i) |
if enabled and loadable then |
listNormalAddon(i) |
end |
end |
end |
}, |
disabled = { |
desc = "List all disabled addons", |
name = "Disabled", |
type = "execute", |
func = function() |
print("|cffffff7fAddon list:|r") |
local count = GetNumAddOns() |
for i = 1, count do |
local _,_,_,enabled,loadable = GetAddOnInfo(i) |
if not enabled or not loadable then |
listNormalAddon(i) |
end |
end |
end |
}, |
lod = { |
desc = "List all LoadOnDemand addons", |
name = "LoadOnDemand", |
type = "execute", |
func = function() |
print("|cffffff7fAddon list:|r") |
local count = GetNumAddOns() |
for i = 1, count do |
if IsAddOnLoadOnDemand(i) then |
listNormalAddon(i) |
end |
end |
end |
}, |
ace1 = { |
desc = "List all addons using Ace1", |
name = "Ace 1.x", |
type = "execute", |
func = function() |
print("|cffffff7fAddon list:|r") |
local count = GetNumAddOns() |
for i = 1, count do |
local dep1, dep2, dep3, dep4 = GetAddOnDependencies(i) |
if dep1 == "Ace" or dep2 == "Ace" or dep3 == "Ace" or dep4 == "Ace" then |
listNormalAddon(i) |
end |
end |
end |
}, |
libs = { |
desc = "List all libraries using AceLibrary", |
name = "Libraries", |
type = "execute", |
func = function() |
if type(AceLibrary) == "table" and type(AceLibrary.libs) == "table" then |
print("|cffffff7fLibrary list:|r") |
for name, data in pairs(AceLibrary.libs) do |
local s |
if data.minor then |
s = " - " .. tostring(name) .. "." .. tostring(data.minor) |
else |
s = " - " .. tostring(name) |
end |
if rawget(AceLibrary(name), 'slashCommand') then |
s = s .. " |cffffff7f(" .. tostring(AceLibrary(name).slashCommand) .. "|cffffff7f)" |
end |
print(s) |
end |
end |
end |
}, |
search = { |
desc = "Search by name", |
name = "Search", |
type = "text", |
usage = "<keyword>", |
input = true, |
get = false, |
set = function(...) |
local arg = { ... } |
for i,v in ipairs(arg) do |
arg[i] = v:gsub('%*', '.*'):gsub('%%', '%%%%'):lower() |
end |
local count = GetNumAddOns() |
for i = 1, count do |
local name = GetAddOnInfo(i) |
local good = true |
for _,v in ipairs(arg) do |
if not name:lower():find(v) then |
good = false |
break |
end |
end |
if good then |
listNormalAddon(i) |
end |
end |
end |
} |
}, |
}, |
enable = { |
desc = "Enable addon(s).", |
name = "Enable", |
type = "text", |
usage = "<addon 1> <addon 2> ...", |
get = false, |
input = true, |
set = function(...) |
for i = 1, select("#", ...) do |
local addon = select(i, ...) |
local name, title, _, enabled, _, reason = GetAddOnInfo(addon) |
if reason == "MISSING" then |
print(("|cffffff7fAce2:|r AddOn %q does not exist."):format(addon)) |
elseif not enabled then |
EnableAddOn(addon) |
print(("|cffffff7fAce2:|r %s is now enabled."):format(addon or name)) |
else |
print(("|cffffff7fAce2:|r %s is already enabled."):format(addon or name)) |
end |
end |
end, |
}, |
disable = { |
desc = "Disable addon(s).", |
name = "Disable", |
type = "text", |
usage = "<addon 1> <addon 2> ...", |
get = false, |
input = true, |
set = function(...) |
for i = 1, select("#", ...) do |
local addon = select(i, ...) |
local name, title, _, enabled, _, reason = GetAddOnInfo(addon) |
if reason == "MISSING" then |
print(("|cffffff7fAce2:|r AddOn %q does not exist."):format(addon)) |
elseif enabled then |
DisableAddOn(addon) |
print(("|cffffff7fAce2:|r %s is now disabled."):format(addon or name)) |
else |
print(("|cffffff7fAce2:|r %s is already disabled."):format(addon or name)) |
end |
end |
end, |
}, |
load = { |
desc = "Load addon(s).", |
name = "Load", |
type = "text", |
usage = "<addon 1> <addon 2> ...", |
get = false, |
input = true, |
set = function(...) |
for i = 1, select("#", ...) do |
local addon = select(i, ...) |
local name, title, _, _, loadable, reason = GetAddOnInfo(addon) |
if reason == "MISSING" then |
print(("|cffffff7fAce2:|r AddOn %q does not exist."):format(addon)) |
elseif not loadable then |
print(("|cffffff7fAce2:|r AddOn %q is not loadable. Reason: %s."):format(addon, reason)) |
else |
LoadAddOn(addon) |
print(("|cffffff7fAce2:|r %s is now loaded."):format(addon or name)) |
end |
end |
end |
}, |
info = { |
desc = "Display information", |
name = "Information", |
type = "execute", |
func = function() |
local mem, threshold = gcinfo() |
print((" - |cffffff7fMemory usage [|r%.3f MiB|cffffff7f]|r"):format(mem / 1024)) |
if threshold then |
print((" - |cffffff7fThreshold [|r%.3f MiB|cffffff7f]|r"):format(threshold / 1024)) |
end |
print((" - |cffffff7fFramerate [|r%.0f fps|cffffff7f]|r"):format(GetFramerate())) |
local bandwidthIn, bandwidthOut, latency = GetNetStats() |
bandwidthIn, bandwidthOut = floor(bandwidthIn * 1024), floor(bandwidthOut * 1024) |
print((" - |cffffff7fLatency [|r%.0f ms|cffffff7f]|r"):format(latency)) |
print((" - |cffffff7fBandwidth in [|r%.0f B/s|cffffff7f]|r"):format(bandwidthIn)) |
print((" - |cffffff7fBandwidth out [|r%.0f B/s|cffffff7f]|r"):format(bandwidthOut)) |
print((" - |cffffff7fTotal addons [|r%d|cffffff7f]|r"):format(GetNumAddOns())) |
print((" - |cffffff7fAce2 addons [|r%d|cffffff7f]|r"):format(#self.addons)) |
local ace = 0 |
local enabled = 0 |
local disabled = 0 |
local lod = 0 |
for i = 1, GetNumAddOns() do |
local dep1, dep2, dep3, dep4 = GetAddOnDependencies(i) |
if dep1 == "Ace" or dep2 == "Ace" or dep3 == "Ace" or dep4 == "Ace" then |
ace = ace + 1 |
end |
if IsAddOnLoadOnDemand(i) then |
lod = lod + 1 |
end |
local isActive, loadable = select(4, GetAddOnInfo(i)) |
if not isActive or not loadable then |
disabled = disabled + 1 |
else |
enabled = enabled + 1 |
end |
end |
print((" - |cffffff7fAce 1.x addons [|r%d|cffffff7f]|r"):format(ace)) |
print((" - |cffffff7fLoadOnDemand addons [|r%d|cffffff7f]|r"):format(lod)) |
print((" - |cffffff7fenabled addons [|r%d|cffffff7f]|r"):format(enabled)) |
print((" - |cffffff7fdisabled addons [|r%d|cffffff7f]|r"):format(disabled)) |
local libs = 0 |
if type(AceLibrary) == "table" and type(AceLibrary.libs) == "table" then |
for _ in pairs(AceLibrary.libs) do |
libs = libs + 1 |
end |
end |
print((" - |cffffff7fAceLibrary instances [|r%d|cffffff7f]|r"):format(libs)) |
end |
} |
} |
}) |
elseif major == "AceModuleCore-2.0" then |
AceModuleCore = instance |
end |
end |
local function activate(self, oldLib, oldDeactivate) |
AceAddon = self |
self.playerLoginFired = oldLib and oldLib.playerLoginFired or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage |
self.addonsToOnEnable = oldLib and oldLib.addonsToOnEnable |
self.addons = oldLib and oldLib.addons or {} |
self.nextAddon = oldLib and oldLib.nextAddon or {} |
self.skipAddon = oldLib and oldLib.skipAddon or {} |
self.addonsStarted = oldLib and oldLib.addonsStarted or {} |
self.addonsEnabled = oldLib and oldLib.addonsEnabled or {} |
if oldDeactivate then |
oldDeactivate(oldLib) |
end |
end |
AceLibrary:Register(AceAddon, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) |
## Interface: 20400 |
## Title: Lib: AceAddon-2.0 |
## Notes: AddOn development framework |
## Author: Ace Development Team |
## LoadOnDemand: 1 |
## X-Website: http://www.wowace.com |
## X-Category: Library |
## X-License: LGPL v2.1 + MIT for AceOO-2.0 |
## Dependencies: AceLibrary, AceOO-2.0 |
AceAddon-2.0.lua |
## Interface: 20400 |
## Title: Lib: AceDB-2.0 |
## Notes: AddOn development framework |
## Author: Ace Development Team |
## LoadOnDemand: 1 |
## X-Website: http://www.wowace.com |
## X-Category: Library |
## X-License: LGPL v2.1 + MIT for AceOO-2.0 |
## Dependencies: AceLibrary, AceEvent-2.0, AceOO-2.0 |
AceDB-2.0.lua |
--[[ |
Name: AceDB-2.0 |
Revision: $Rev: 56534 $ |
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) |
Inspired By: Ace 1.x by Turan (turan@gryphon.com) |
Website: http://www.wowace.com/ |
Documentation: http://www.wowace.com/index.php/AceDB-2.0 |
SVN: http://svn.wowace.com/root/trunk/Ace2/AceDB-2.0 |
Description: Mixin to allow for fast, clean, and featureful saved variable |
access. |
Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0 |
License: LGPL v2.1 |
]] |
local MAJOR_VERSION = "AceDB-2.0" |
local MINOR_VERSION = "$Revision: 56534 $" |
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end |
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end |
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end |
local function safecall(func,...) |
local success, err = pcall(func,...) |
if not success then geterrorhandler()(err) end |
end |
local ACTIVE, ENABLED, STATE, TOGGLE_ACTIVE, MAP_ACTIVESUSPENDED, SET_PROFILE, SET_PROFILE_USAGE, PROFILE, PLAYER_OF_REALM, CHOOSE_PROFILE_DESC, CHOOSE_PROFILE_GUI, COPY_PROFILE_DESC, COPY_PROFILE_GUI, OTHER_PROFILE_DESC, OTHER_PROFILE_GUI, OTHER_PROFILE_USAGE, RESET_PROFILE, RESET_PROFILE_DESC, CHARACTER_COLON, REALM_COLON, CLASS_COLON, DEFAULT, ALTERNATIVE |
-- Move these into "enUS" when they've been translated in all other locales |
local DELETE_PROFILE = "Delete" |
local DELETE_PROFILE_DESC = "Deletes a profile. Note that no check is made whether this profile is in use by other characters or not." |
local DELETE_PROFILE_USAGE = "<profile name>" |
if GetLocale() == "deDE" then |
DELETE_PROFILE = "L\195\182schen" |
DELETE_PROFILE_DESC = "L\195\182scht ein Profil. Beachte das nicht \195\188berpr\195\188ft wird ob das zu l\195\182schende Profil von anderen Charakteren genutzt wird oder nicht." |
DELETE_PROFILE_USAGE = "<profil name>" |
ACTIVE = "Aktiv" |
ENABLED = "Aktiviert" |
STATE = "Status" |
TOGGLE_ACTIVE = "Stoppt/Aktiviert dieses Addon." |
MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Aktiv|r", [false] = "|cffff0000Gestoppt|r" } |
SET_PROFILE = "Setzt das Profil f\195\188r dieses Addon." |
SET_PROFILE_USAGE = "{Charakter || Klasse || Realm || <Profilname>}" |
PROFILE = "Profil" |
PLAYER_OF_REALM = "%s von %s" |
CHOOSE_PROFILE_DESC = "W\195\164hle ein Profil." |
CHOOSE_PROFILE_GUI = "W\195\164hle" |
COPY_PROFILE_DESC = "Kopiert Einstellungen von einem anderem Profil." |
COPY_PROFILE_GUI = "Kopiere von" |
OTHER_PROFILE_DESC = "W\195\164hle ein anderes Profil." |
OTHER_PROFILE_GUI = "Anderes" |
OTHER_PROFILE_USAGE = "<Profilname>" |
RESET_PROFILE = "Resette das Profil" |
RESET_PROFILE_DESC = "Entfernt alle Einstellungen des gegenw\195\164rtigen Profils." |
CHARACTER_COLON = "Charakter: " |
REALM_COLON = "Realm: " |
CLASS_COLON = "Klasse: " |
DEFAULT = "Vorgabe" |
ALTERNATIVE = "Alternativ" |
elseif GetLocale() == "frFR" then |
ACTIVE = "Actif" |
ENABLED = "Activ\195\169" |
STATE = "Etat" |
TOGGLE_ACTIVE = "Suspend/active cet addon." |
MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Actif|r", [false] = "|cffff0000Suspendu|r" } |
SET_PROFILE = "S\195\169lectionne le profil pour cet addon." |
SET_PROFILE_USAGE = "{perso || classe || royaume || <nom de profil>}" |
PROFILE = "Profil" |
PLAYER_OF_REALM = "%s de %s" |
CHOOSE_PROFILE_DESC = "Choisissez un profil." |
CHOOSE_PROFILE_GUI = "Choix" |
COPY_PROFILE_DESC = "Copier les param\195\168tres d'un autre profil." |
COPY_PROFILE_GUI = "Copier \195\160 partir de" |
OTHER_PROFILE_DESC = "Choisissez un autre profil." |
OTHER_PROFILE_GUI = "Autre" |
OTHER_PROFILE_USAGE = "<nom de profil>" |
RESET_PROFILE = "Reset profile" -- fix |
RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix |
CHARACTER_COLON = "Personnage: " |
REALM_COLON = "Royaume: " |
CLASS_COLON = "Classe: " |
DEFAULT = "Default" -- fix |
ALTERNATIVE = "Alternative" -- fix |
elseif GetLocale() == "koKR" then |
DELETE_PROFILE = "ìì " |
DELETE_PROFILE_DESC = "íë¡íì ìì í©ëë¤." |
DELETE_PROFILE_USAGE = "<íë¡íëª >" |
ACTIVE = "ì¬ì©" |
ENABLED = "ì¬ì©" |
STATE = "ìí" |
TOGGLE_ACTIVE = "ì´ ì ëì¨ ì¤ì§/ë¤ì ìì" |
MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00ì¬ì©|r", [false] = "|cffff0000ì¤ì§|r" } |
SET_PROFILE = "ì´ ì ëì¨ì íë¡í ì¤ì " |
SET_PROFILE_USAGE = "{ìºë¦í°ëª || ì§ì || ìë²ëª || <íë¡íëª >}" |
PROFILE = "íë¡í" |
PLAYER_OF_REALM = "%s (%s ìë²)" |
CHOOSE_PROFILE_DESC = "íë¡íì ì íí©ëë¤." |
CHOOSE_PROFILE_GUI = "ì í" |
COPY_PROFILE_DESC = "ë¤ë¥¸ íë¡í ì¤ì ì ë³µì¬í©ëë¤." |
COPY_PROFILE_GUI = "ë³µì¬" |
OTHER_PROFILE_DESC = "ë¤ë¥¸ íë¡íì ì íí©ëë¤." |
OTHER_PROFILE_GUI = "기í" |
OTHER_PROFILE_USAGE = "<íë¡íëª >" |
RESET_PROFILE = "íë¡í ì´ê¸°í" |
RESET_PROFILE_DESC = "모ë ì¸í ìì íì¬ íë¡íì ì´ê¸°í í©ëë¤." |
CHARACTER_COLON = "ìºë¦í°: " |
REALM_COLON = "ìë²: " |
CLASS_COLON = "ì§ì : " |
DEFAULT = "기본ê°" |
ALTERNATIVE = "ëì²´" |
elseif GetLocale() == "zhTW" then |
DELETE_PROFILE = "åªé¤" |
DELETE_PROFILE_DESC = "åªé¤è¨éæªã注æï¼æå¯è½å¥çè§è²ä¹ä½¿ç¨éåè¨éæªã" |
DELETE_PROFILE_USAGE = "<è¨éæªå稱>" |
ACTIVE = "åå" |
ENABLED = "åç¨" |
STATE = "çæ " |
TOGGLE_ACTIVE = "æ«å/ç¹¼çºä½¿ç¨éåæ件ã" |
MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00åå|r", [false] = "|cffff0000å·²æ«å|r" } |
SET_PROFILE = "è¨å®éæ件çè¨éæªã" |
SET_PROFILE_USAGE = "{è§è² || è·æ¥ || 伺æå¨ || <è¨éæªå稱>}" |
PROFILE = "è¨éæª" |
PLAYER_OF_REALM = "%s - %s" |
CHOOSE_PROFILE_DESC = "é¸æä¸åè¨éæªã" |
CHOOSE_PROFILE_GUI = "é¸æ" |
COPY_PROFILE_DESC = "ç±å ¶ä»è¨éæªè¤è£½è¨å®ã" |
COPY_PROFILE_GUI = "è¤è£½èª" |
OTHER_PROFILE_DESC = "é¸æå ¶ä»è¨éæªã" |
OTHER_PROFILE_GUI = "å ¶ä»" |
OTHER_PROFILE_USAGE = "<è¨éæªå稱>" |
RESET_PROFILE = "éè¨è¨éæª" |
RESET_PROFILE_DESC = "æ¸ é¤ç®åçè¨éæªä¸çææè¨å®ã" |
CHARACTER_COLON = "è§è²: " |
REALM_COLON = "伺æå¨: " |
CLASS_COLON = "è·æ¥: " |
DEFAULT = "é è¨" |
ALTERNATIVE = "æ¿ä»£" |
elseif GetLocale() == "zhCN" then |
ACTIVE = "\230\156\137\230\149\136" |
ENABLED = "\229\144\175\231\148\168" |
STATE = "\231\138\182\230\128\129" |
TOGGLE_ACTIVE = "\230\154\130\229\129\156/\230\129\162\229\164\141 \230\173\164\230\143\146\228\187\182." |
MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00\230\156\137\230\149\136|r", [false] = "|cffff0000\230\154\130\229\129\156|r" } |
SET_PROFILE = "\232\174\190\231\189\174\233\133\141\231\189\174\230\150\135\228\187\182\228\184\186\232\191\153\230\143\146\228\187\182." |
SET_PROFILE_USAGE = "{\229\173\151\231\172\166 || \233\128\137\228\187\182\231\177\187 || \229\159\159 || <\233\133\141\231\189\174\230\150\135\228\187\182\229\144\141\229\173\151>}" |
PROFILE = "\233\133\141\231\189\174\230\150\135\228\187\182" |
PLAYER_OF_REALM = "%s \231\154\132 %s" |
CHOOSE_PROFILE_DESC = "\233\128\137\230\139\169\233\133\141\231\189\174\230\150\135\228\187\182." |
CHOOSE_PROFILE_GUI = "\233\128\137\230\139\169" |
COPY_PROFILE_DESC = "\229\164\141\229\136\182\232\174\190\231\189\174\228\187\142\229\143\166\228\184\128\228\184\170\233\133\141\231\189\174\230\150\135\228\187\182." |
COPY_PROFILE_GUI = "\229\164\141\229\136\182\228\187\142" |
OTHER_PROFILE_DESC = "\233\128\137\230\139\169\229\143\166\228\184\128\228\184\170\233\133\141\231\189\174\230\150\135\228\187\182." |
OTHER_PROFILE_GUI = "\229\133\182\228\187\150" |
OTHER_PROFILE_USAGE = "<\233\133\141\231\189\174\230\150\135\228\187\182\229\144\141\229\173\151>" |
RESET_PROFILE = "Reset profile" -- fix |
RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix |
CHARACTER_COLON = "\229\173\151\231\172\166: " |
REALM_COLON = "\229\159\159: " |
CLASS_COLON = "\233\128\137\228\187\182\231\177\187: " |
DEFAULT = "Default" -- fix |
ALTERNATIVE = "Alternative" -- fix |
elseif GetLocale() == "esES" then |
ACTIVE = "Activo" |
ENABLED = "Activado" |
STATE = "Estado" |
TOGGLE_ACTIVE = "Parar/Continuar este accesorio" |
MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Activo|r", [false] = "|cffff0000Parado|r" } |
SET_PROFILE = "Selecciona el perfil para este accesorio." |
SET_PROFILE_USAGE = "{perso || clase || reino || <nombre del perfil>}" |
PROFILE = "Perfil" |
PLAYER_OF_REALM = "%s de %s" |
CHOOSE_PROFILE_DESC = "Elige un perfil." |
CHOOSE_PROFILE_GUI = "Elige" |
COPY_PROFILE_DESC = "Copiar de un perfil a otro" |
COPY_PROFILE_GUI = "Copiar desde" |
OTHER_PROFILE_DESC = "Elige otro perfil." |
OTHER_PROFILE_GUI = "Otro" |
OTHER_PROFILE_USAGE = "<nombre del perfil>" |
RESET_PROFILE = "Reset profile" -- fix |
RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix |
CHARACTER_COLON = "Personaje: " |
REALM_COLON = "Reino: " |
CLASS_COLON = "Clase: " |
DEFAULT = "Por defecto" |
ALTERNATIVE = "Alternativo" |
else -- enUS |
ACTIVE = "Active" |
ENABLED = "Enabled" |
STATE = "State" |
TOGGLE_ACTIVE = "Suspend/resume this addon." |
MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Active|r", [false] = "|cffff0000Suspended|r" } |
SET_PROFILE = "Set profile for this addon." |
SET_PROFILE_USAGE = "{char || class || realm || <profile name>}" |
PROFILE = "Profile" |
PLAYER_OF_REALM = "%s of %s" |
CHOOSE_PROFILE_DESC = "Choose a profile." |
CHOOSE_PROFILE_GUI = "Choose" |
COPY_PROFILE_DESC = "Copy settings from another profile." |
COPY_PROFILE_GUI = "Copy from" |
OTHER_PROFILE_DESC = "Choose another profile." |
OTHER_PROFILE_GUI = "Other" |
OTHER_PROFILE_USAGE = "<profile name>" |
RESET_PROFILE = "Reset profile" |
RESET_PROFILE_DESC = "Clear all settings of the current profile." |
CHARACTER_COLON = "Character: " |
REALM_COLON = "Realm: " |
CLASS_COLON = "Class: " |
DEFAULT = "Default" |
ALTERNATIVE = "Alternative" |
end |
local convertFromOldCharID |
do |
local matchStr = "^" .. PLAYER_OF_REALM:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1"):gsub("%%s", "(.+)") .. "$" |
function convertFromOldCharID(str) |
local player, realm = str:match(matchStr) |
if not player then |
return str |
end |
return player .. " - " .. realm |
end |
end |
local AceOO = AceLibrary("AceOO-2.0") |
local AceEvent |
local Mixin = AceOO.Mixin |
local AceDB = Mixin { |
"RegisterDB", |
"RegisterDefaults", |
"ResetDB", |
"SetProfile", |
"GetProfile", |
"CopyProfileFrom", |
"DeleteProfile", |
"ToggleActive", |
"IsActive", |
"AcquireDBNamespace", |
} |
local Dewdrop = AceLibrary:HasInstance("Dewdrop-2.0") and AceLibrary("Dewdrop-2.0") |
local _G = getfenv(0) |
local function inheritDefaults(t, defaults) |
if not defaults then |
return t |
end |
for k,v in pairs(defaults) do |
if k == "*" or k == "**" then |
local v = v |
if type(v) == "table" then |
setmetatable(t, { |
__index = function(self, key) |
if key == nil then |
return nil |
end |
self[key] = {} |
inheritDefaults(self[key], v) |
return self[key] |
end |
} ) |
else |
setmetatable(t, { |
__index = function(self, key) |
if key == nil then |
return nil |
end |
self[key] = v |
return self[key] |
end |
} ) |
end |
for key in pairs(t) do |
if (defaults[key] == nil or key == k) and type(t[key]) == "table" then |
inheritDefaults(t[key], v) |
end |
end |
else |
if type(v) == "table" then |
if type(rawget(t, k)) ~= "table" then |
t[k] = {} |
end |
inheritDefaults(t[k], v) |
if defaults["**"] then |
inheritDefaults(t[k], defaults["**"]) |
end |
elseif rawget(t, k) == nil then |
t[k] = v |
end |
end |
end |
return t |
end |
local _,race = UnitRace("player") |
local faction |
if race == "Orc" or race == "Scourge" or race == "Troll" or race == "Tauren" or race == "BloodElf" then |
faction = FACTION_HORDE |
else |
faction = FACTION_ALLIANCE |
end |
local server = GetRealmName():trim() |
local charID = UnitName("player") .. " - " .. server |
local realmID = server .. " - " .. faction |
local classID = UnitClass("player") |
AceDB.CHAR_ID = charID |
AceDB.REALM_ID = realmID |
AceDB.CLASS_ID = classID |
AceDB.FACTION = faction |
AceDB.REALM = server |
AceDB.NAME = UnitName("player") |
local new, del |
do |
local list = setmetatable({}, {__mode="k"}) |
function new() |
local t = next(list) |
if t then |
list[t] = nil |
return t |
else |
return {} |
end |
end |
function del(t) |
setmetatable(t, nil) |
for k in pairs(t) do |
t[k] = nil |
end |
list[t] = true |
end |
end |
local caseInsensitive_mt = { |
__index = function(self, key) |
if type(key) ~= "string" then |
return nil |
end |
local lowerKey = key:lower() |
for k,v in pairs(self) do |
if k:lower() == lowerKey then |
return self[k] |
end |
end |
end, |
__newindex = function(self, key, value) |
if type(key) ~= "string" then |
return error("table index is nil", 2) |
end |
local lowerKey = key:lower() |
for k in pairs(self) do |
if k:lower() == lowerKey then |
rawset(self, k, nil) |
rawset(self, key, value) |
return |
end |
end |
rawset(self, key, value) |
end |
} |
local db_mt = { __index = function(db, key) |
if key == "char" then |
if db.charName then |
if type(_G[db.charName]) ~= "table" then |
_G[db.charName] = {} |
end |
if type(_G[db.charName].global) ~= "table" then |
_G[db.charName].global = {} |
end |
rawset(db, 'char', _G[db.charName].global) |
else |
if type(db.raw.chars) ~= "table" then |
db.raw.chars = {} |
end |
local id = charID |
if type(db.raw.chars[id]) ~= "table" then |
db.raw.chars[id] = {} |
end |
rawset(db, 'char', db.raw.chars[id]) |
end |
if db.defaults and db.defaults.char then |
inheritDefaults(db.char, db.defaults.char) |
end |
return db.char |
elseif key == "realm" then |
if type(db.raw.realms) ~= "table" then |
db.raw.realms = {} |
end |
local id = realmID |
if type(db.raw.realms[id]) ~= "table" then |
db.raw.realms[id] = {} |
end |
rawset(db, 'realm', db.raw.realms[id]) |
if db.defaults and db.defaults.realm then |
inheritDefaults(db.realm, db.defaults.realm) |
end |
return db.realm |
elseif key == "server" then |
if type(db.raw.servers) ~= "table" then |
db.raw.servers = {} |
end |
local id = server |
if type(db.raw.servers[id]) ~= "table" then |
db.raw.servers[id] = {} |
end |
rawset(db, 'server', db.raw.servers[id]) |
if db.defaults and db.defaults.server then |
inheritDefaults(db.server, db.defaults.server) |
end |
return db.server |
elseif key == "account" then |
if type(db.raw.account) ~= "table" then |
db.raw.account = {} |
end |
rawset(db, 'account', db.raw.account) |
if db.defaults and db.defaults.account then |
inheritDefaults(db.account, db.defaults.account) |
end |
return db.account |
elseif key == "faction" then |
if type(db.raw.factions) ~= "table" then |
db.raw.factions = {} |
end |
local id = faction |
if type(db.raw.factions[id]) ~= "table" then |
db.raw.factions[id] = {} |
end |
rawset(db, 'faction', db.raw.factions[id]) |
if db.defaults and db.defaults.faction then |
inheritDefaults(db.faction, db.defaults.faction) |
end |
return db.faction |
elseif key == "class" then |
if type(db.raw.classes) ~= "table" then |
db.raw.classes = {} |
end |
local id = classID |
if type(db.raw.classes[id]) ~= "table" then |
db.raw.classes[id] = {} |
end |
rawset(db, 'class', db.raw.classes[id]) |
if db.defaults and db.defaults.class then |
inheritDefaults(db.class, db.defaults.class) |
end |
return db.class |
elseif key == "profile" then |
if type(db.raw.profiles) ~= "table" then |
db.raw.profiles = setmetatable({}, caseInsensitive_mt) |
else |
setmetatable(db.raw.profiles, caseInsensitive_mt) |
end |
local id = db.raw.currentProfile[charID] |
if id == "char" then |
id = "char/" .. charID |
elseif id == "class" then |
id = "class/" .. classID |
elseif id == "realm" then |
id = "realm/" .. realmID |
end |
if type(db.raw.profiles[id]) ~= "table" then |
db.raw.profiles[id] = {} |
end |
rawset(db, 'profile', db.raw.profiles[id]) |
if db.defaults and db.defaults.profile then |
inheritDefaults(db.profile, db.defaults.profile) |
end |
return db.profile |
elseif key == "raw" or key == "defaults" or key == "name" or key == "charName" or key == "namespaces" then |
return nil |
end |
error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2) |
end, __newindex = function(db, key, value) |
error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2) |
end } |
local function RecalculateAceDBCopyFromList(target) |
local db = target.db |
local t = target['acedb-profile-copylist'] |
for k,v in pairs(t) do |
t[k] = nil |
end |
local _,currentProfile = AceDB.GetProfile(target) |
if db and db.raw then |
if db.raw.profiles then |
for k in pairs(db.raw.profiles) do |
if currentProfile ~= k then |
if k:find("^char/") then |
local name = k:sub(6) |
local player, realm = name:match("^(.*) %- (.*)$") |
if player then |
name = PLAYER_OF_REALM:format(player, realm) |
end |
t[k] = CHARACTER_COLON .. name |
elseif k:find("^realm/") then |
local name = k:sub(7) |
t[k] = REALM_COLON .. name |
elseif k:find("^class/") then |
local name = k:sub(7) |
t[k] = CLASS_COLON .. name |
else |
t[k] = k |
end |
end |
end |
end |
if db.raw.namespaces then |
for _,n in pairs(db.raw.namespaces) do |
if n.profiles then |
for k in pairs(n.profiles) do |
if currentProfile ~= k then |
if k:find('^char/') then |
local name = k:sub(6) |
local player, realm = name:match("^(.*) %- (.*)$") |
if player then |
name = PLAYER_OF_REALM:format(player, realm) |
end |
t[k] = CHARACTER_COLON .. name |
elseif k:find('^realm/') then |
local name = k:sub(7) |
t[k] = REALM_COLON .. name |
elseif k:find('^class/') then |
local name = k:sub(7) |
t[k] = CLASS_COLON .. name |
else |
t[k] = k |
end |
end |
end |
end |
end |
end |
end |
if t.Default then |
t.Default = DEFAULT |
end |
if t.Alternative then |
t.Alternative = ALTERNATIVE |
end |
end |
local function RecalculateAceDBProfileList(target) |
local t = target['acedb-profile-list'] |
for k,v in pairs(t) do |
t[k] = nil |
end |
t.char = CHARACTER_COLON .. PLAYER_OF_REALM:format(UnitName("player"), server) |
t.realm = REALM_COLON .. realmID |
t.class = CLASS_COLON .. classID |
t.Default = DEFAULT |
local db = target.db |
if db and db.raw then |
if db.raw.profiles then |
for k in pairs(db.raw.profiles) do |
if not k:find("^char/") and not k:find("^realm/") and not k:find("^class/") then |
t[k] = k |
end |
end |
end |
if db.raw.namespaces then |
for _,n in pairs(db.raw.namespaces) do |
if n.profiles then |
for k in pairs(n.profiles) do |
if not k:find("^char/") and not k:find("^realm/") and not k:find("^class/") then |
t[k] = k |
end |
end |
end |
end |
end |
local curr = db.raw.currentProfile and db.raw.currentProfile[charID] |
if curr and not t[curr] then |
t[curr] = curr |
end |
end |
if t.Alternative then |
t.Alternative = ALTERNATIVE |
end |
end |
local CrawlForSerialization |
local CrawlForDeserialization |
local function SerializeObject(o) |
local t = { o:Serialize() } |
CrawlForSerialization(t) |
t[0] = o.class:GetLibraryVersion() |
return t |
end |
local function DeserializeObject(t) |
CrawlForDeserialization(t) |
local className = t[0] |
t[0] = nil |
return AceLibrary(className):Deserialize(unpack(t)) |
end |
local function IsSerializable(t) |
return AceOO.inherits(t, AceOO.Class) and t.class and type(t.class.Deserialize) == "function" and type(t.Serialize) == "function" and type(t.class.GetLibraryVersion) == "function" |
end |
function CrawlForSerialization(t) |
local tmp = new() |
for k,v in pairs(t) do |
tmp[k] = v |
end |
for k,v in pairs(tmp) do |
if type(v) == "table" and type(rawget(v, 0)) ~= "userdata" then |
if IsSerializable(v) then |
v = SerializeObject(v) |
t[k] = v |
else |
CrawlForSerialization(v) |
end |
end |
if type(k) == "table" and type(rawget(k, 0)) ~= "userdata" then |
if IsSerializable(k) then |
t[k] = nil |
t[SerializeObject(k)] = v |
else |
CrawlForSerialization(k) |
end |
end |
tmp[k] = nil |
k = nil |
end |
tmp = del(tmp) |
end |
local function IsDeserializable(t) |
return type(rawget(t, 0)) == "string" and AceLibrary:HasInstance(rawget(t, 0)) |
end |
function CrawlForDeserialization(t) |
local tmp = new() |
for k,v in pairs(t) do |
tmp[k] = v |
end |
for k,v in pairs(tmp) do |
if type(v) == "table" then |
if IsDeserializable(v) then |
t[k] = DeserializeObject(v) |
del(v) |
v = t[k] |
elseif type(rawget(v, 0)) ~= "userdata" then |
CrawlForDeserialization(v) |
end |
end |
if type(k) == "table" then |
if IsDeserializable(k) then |
t[k] = nil |
t[DeserializeObject(k)] = v |
del(k) |
elseif type(rawget(k, 0)) ~= "userdata" then |
CrawlForDeserialization(k) |
end |
end |
tmp[k] = nil |
k = nil |
end |
tmp = del(tmp) |
end |
local namespace_mt = { __index = function(namespace, key) |
local db = namespace.db |
local name = namespace.name |
if key == "char" then |
if db.charName then |
if type(_G[db.charName]) ~= "table" then |
_G[db.charName] = {} |
end |
if type(_G[db.charName].namespaces) ~= "table" then |
_G[db.charName].namespaces = {} |
end |
if type(_G[db.charName].namespaces[name]) ~= "table" then |
_G[db.charName].namespaces[name] = {} |
end |
rawset(namespace, 'char', _G[db.charName].namespaces[name]) |
else |
if type(db.raw.namespaces) ~= "table" then |
db.raw.namespaces = {} |
end |
if type(db.raw.namespaces[name]) ~= "table" then |
db.raw.namespaces[name] = {} |
end |
if type(db.raw.namespaces[name].chars) ~= "table" then |
db.raw.namespaces[name].chars = {} |
end |
local id = charID |
if type(db.raw.namespaces[name].chars[id]) ~= "table" then |
db.raw.namespaces[name].chars[id] = {} |
end |
rawset(namespace, 'char', db.raw.namespaces[name].chars[id]) |
end |
if namespace.defaults and namespace.defaults.char then |
inheritDefaults(namespace.char, namespace.defaults.char) |
end |
return namespace.char |
elseif key == "realm" then |
if type(db.raw.namespaces) ~= "table" then |
db.raw.namespaces = {} |
end |
if type(db.raw.namespaces[name]) ~= "table" then |
db.raw.namespaces[name] = {} |
end |
if type(db.raw.namespaces[name].realms) ~= "table" then |
db.raw.namespaces[name].realms = {} |
end |
local id = realmID |
if type(db.raw.namespaces[name].realms[id]) ~= "table" then |
db.raw.namespaces[name].realms[id] = {} |
end |
rawset(namespace, 'realm', db.raw.namespaces[name].realms[id]) |
if namespace.defaults and namespace.defaults.realm then |
inheritDefaults(namespace.realm, namespace.defaults.realm) |
end |
return namespace.realm |
elseif key == "server" then |
if type(db.raw.namespaces) ~= "table" then |
db.raw.namespaces = {} |
end |
if type(db.raw.namespaces[name]) ~= "table" then |
db.raw.namespaces[name] = {} |
end |
if type(db.raw.namespaces[name].servers) ~= "table" then |
db.raw.namespaces[name].servers = {} |
end |
local id = server |
if type(db.raw.namespaces[name].servers[id]) ~= "table" then |
db.raw.namespaces[name].servers[id] = {} |
end |
rawset(namespace, 'server', db.raw.namespaces[name].servers[id]) |
if namespace.defaults and namespace.defaults.server then |
inheritDefaults(namespace.server, namespace.defaults.server) |
end |
return namespace.server |
elseif key == "account" then |
if type(db.raw.namespaces) ~= "table" then |
db.raw.namespaces = {} |
end |
if type(db.raw.namespaces[name]) ~= "table" then |
db.raw.namespaces[name] = {} |
end |
if type(db.raw.namespaces[name].account) ~= "table" then |
db.raw.namespaces[name].account = {} |
end |
rawset(namespace, 'account', db.raw.namespaces[name].account) |
if namespace.defaults and namespace.defaults.account then |
inheritDefaults(namespace.account, namespace.defaults.account) |
end |
return namespace.account |
elseif key == "faction" then |
if type(db.raw.namespaces) ~= "table" then |
db.raw.namespaces = {} |
end |
if type(db.raw.namespaces[name]) ~= "table" then |
db.raw.namespaces[name] = {} |
end |
if type(db.raw.namespaces[name].factions) ~= "table" then |
db.raw.namespaces[name].factions = {} |
end |
local id = faction |
if type(db.raw.namespaces[name].factions[id]) ~= "table" then |
db.raw.namespaces[name].factions[id] = {} |
end |
rawset(namespace, 'faction', db.raw.namespaces[name].factions[id]) |
if namespace.defaults and namespace.defaults.faction then |
inheritDefaults(namespace.faction, namespace.defaults.faction) |
end |
return namespace.faction |
elseif key == "class" then |
if type(db.raw.namespaces) ~= "table" then |
db.raw.namespaces = {} |
end |
if type(db.raw.namespaces[name]) ~= "table" then |
db.raw.namespaces[name] = {} |
end |
if type(db.raw.namespaces[name].classes) ~= "table" then |
db.raw.namespaces[name].classes = {} |
end |
local id = classID |
if type(db.raw.namespaces[name].classes[id]) ~= "table" then |
db.raw.namespaces[name].classes[id] = {} |
end |
rawset(namespace, 'class', db.raw.namespaces[name].classes[id]) |
if namespace.defaults and namespace.defaults.class then |
inheritDefaults(namespace.class, namespace.defaults.class) |
end |
return namespace.class |
elseif key == "profile" then |
if type(db.raw.namespaces) ~= "table" then |
db.raw.namespaces = {} |
end |
if type(db.raw.namespaces[name]) ~= "table" then |
db.raw.namespaces[name] = {} |
end |
if type(db.raw.namespaces[name].profiles) ~= "table" then |
db.raw.namespaces[name].profiles = setmetatable({}, caseInsensitive_mt) |
else |
setmetatable(db.raw.namespaces[name].profiles, caseInsensitive_mt) |
end |
local id = db.raw.currentProfile[charID] |
if id == "char" then |
id = "char/" .. charID |
elseif id == "class" then |
id = "class/" .. classID |
elseif id == "realm" then |
id = "realm/" .. realmID |
end |
if type(db.raw.namespaces[name].profiles[id]) ~= "table" then |
db.raw.namespaces[name].profiles[id] = {} |
end |
rawset(namespace, 'profile', db.raw.namespaces[name].profiles[id]) |
if namespace.defaults and namespace.defaults.profile then |
inheritDefaults(namespace.profile, namespace.defaults.profile) |
end |
return namespace.profile |
elseif key == "defaults" or key == "name" or key == "db" then |
return nil |
end |
error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2) |
end, __newindex = function(db, key, value) |
error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2) |
end } |
local tmp = {} |
function AceDB:InitializeDB(addonName) |
local db = self.db |
if not db then |
if addonName then |
AceDB.addonsLoaded[addonName] = true |
end |
return |
end |
if db.raw then |
-- someone manually initialized |
return |
end |
if type(_G[db.name]) ~= "table" then |
_G[db.name] = {} |
else |
CrawlForDeserialization(_G[db.name]) |
end |
if db.charName then |
if type(_G[db.charName]) ~= "table" then |
_G[db.charName] = {} |
else |
CrawlForDeserialization(_G[db.charName]) |
end |
end |
rawset(db, 'raw', _G[db.name]) |
if not db.raw.currentProfile then |
db.raw.currentProfile = {} |
else |
for k,v in pairs(db.raw.currentProfile) do |
tmp[convertFromOldCharID(k)] = v |
db.raw.currentProfile[k] = nil |
end |
for k,v in pairs(tmp) do |
db.raw.currentProfile[k] = v |
tmp[k] = nil |
end |
end |
if not db.raw.currentProfile[charID] then |
db.raw.currentProfile[charID] = AceDB.registry[self] or "Default" |
end |
if db.raw.profiles then |
for k,v in pairs(db.raw.profiles) do |
local new_k = k |
if k:find("^char/") then |
new_k = "char/" .. convertFromOldCharID(k:sub(6)) |
end |
tmp[new_k] = v |
db.raw.profiles[k] = nil |
end |
for k,v in pairs(tmp) do |
db.raw.profiles[k] = v |
tmp[k] = nil |
end |
end |
if db.raw.disabledModules then -- AceModuleCore-2.0 |
for k,v in pairs(db.raw.disabledModules) do |
local new_k = k |
if k:find("^char/") then |
new_k = "char/" .. convertFromOldCharID(k:sub(6)) |
end |
tmp[new_k] = v |
db.raw.disabledModules[k] = nil |
end |
for k,v in pairs(tmp) do |
db.raw.disabledModules[k] = v |
tmp[k] = nil |
end |
end |
if db.raw.chars then |
for k,v in pairs(db.raw.chars) do |
tmp[convertFromOldCharID(k)] = v |
db.raw.chars[k] = nil |
end |
for k,v in pairs(tmp) do |
db.raw.chars[k] = v |
tmp[k] = nil |
end |
end |
if db.raw.namespaces then |
for l,u in pairs(db.raw.namespaces) do |
if u.chars then |
for k,v in pairs(u.chars) do |
tmp[convertFromOldCharID(k)] = v |
u.chars[k] = nil |
end |
for k,v in pairs(tmp) do |
u.chars[k] = v |
tmp[k] = nil |
end |
end |
end |
end |
if db.raw.disabled then |
setmetatable(db.raw.disabled, caseInsensitive_mt) |
end |
if self['acedb-profile-copylist'] then |
RecalculateAceDBCopyFromList(self) |
end |
if self['acedb-profile-list'] then |
RecalculateAceDBProfileList(self) |
end |
setmetatable(db, db_mt) |
end |
function AceDB:OnEmbedInitialize(target, name) |
if name then |
self:ADDON_LOADED(name) |
end |
self.InitializeDB(target, name) |
end |
function AceDB:RegisterDB(name, charName, defaultProfile) |
AceDB:argCheck(name, 2, "string") |
AceDB:argCheck(charName, 3, "string", "nil") |
AceDB:argCheck(defaultProfile, 4, "string", "nil") |
if self.db then |
AceDB:error("Cannot call \"RegisterDB\" if self.db is set.") |
end |
local stack = debugstack() |
local addonName = stack:gsub(".-\n.-\\AddOns\\(.-)\\.*", "%1") |
self.db = { |
name = name, |
charName = charName |
} |
AceDB.registry[self] = defaultProfile or "Default" |
if AceDB.addonsLoaded[addonName] then |
AceDB.InitializeDB(self, addonName) |
else |
AceDB.addonsToBeInitialized[self] = addonName |
end |
end |
function AceDB:RegisterDefaults(kind, defaults, a3) |
local name |
if a3 then |
name, kind, defaults = kind, defaults, a3 |
AceDB:argCheck(name, 2, "string") |
AceDB:argCheck(kind, 3, "string") |
AceDB:argCheck(defaults, 4, "table") |
else |
AceDB:argCheck(kind, 2, "string") |
AceDB:argCheck(defaults, 3, "table") |
end |
if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" and kind ~= "faction" and kind ~= "server" then |
AceDB:error("Bad argument #%d to `RegisterDefaults' (\"char\", \"class\", \"profile\", \"account\", \"realm\", \"server\", or \"faction\" expected, got %q)", a3 and 3 or 2, kind) |
end |
if type(self.db) ~= "table" or type(self.db.name) ~= "string" then |
AceDB:error("Cannot call \"RegisterDefaults\" unless \"RegisterDB\" has been previously called.") |
end |
local db |
if name then |
local namespace = self:AcquireDBNamespace(name) |
if namespace.defaults and namespace.defaults[kind] then |
AceDB:error("\"RegisterDefaults\" has already been called for %q::%q.", name, kind) |
end |
db = namespace |
else |
if self.db.defaults and self.db.defaults[kind] then |
AceDB:error("\"RegisterDefaults\" has already been called for %q.", kind) |
end |
db = self.db |
end |
if not db.defaults then |
rawset(db, 'defaults', {}) |
end |
db.defaults[kind] = defaults |
if rawget(db, kind) then |
inheritDefaults(db[kind], defaults) |
end |
end |
function AceDB:ResetDB(kind, a2) |
local name |
if a2 then |
name, kind = kind, a2 |
AceDB:argCheck(name, 2, "nil", "string") |
AceDB:argCheck(kind, 3, "nil", "string") |
else |
AceDB:argCheck(kind, 2, "nil", "string") |
if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" and kind ~= "faction" and kind ~= "server" then |
name, kind = kind, nil |
end |
end |
if not self.db or not self.db.raw then |
AceDB:error("Cannot call \"ResetDB\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.") |
end |
local db = self.db |
if not kind then |
if not name then |
if db.charName then |
_G[db.charName] = nil |
end |
_G[db.name] = nil |
rawset(db, 'raw', nil) |
AceDB.InitializeDB(self) |
if db.namespaces then |
for name,v in pairs(db.namespaces) do |
rawset(v, 'account', nil) |
rawset(v, 'char', nil) |
rawset(v, 'class', nil) |
rawset(v, 'profile', nil) |
rawset(v, 'realm', nil) |
rawset(v, 'server', nil) |
rawset(v, 'faction', nil) |
end |
end |
else |
if db.raw.namespaces then |
db.raw.namespaces[name] = nil |
end |
if db.namespaces then |
local v = db.namespaces[name] |
if v then |
rawset(v, 'account', nil) |
rawset(v, 'char', nil) |
rawset(v, 'class', nil) |
rawset(v, 'profile', nil) |
rawset(v, 'realm', nil) |
rawset(v, 'server', nil) |
rawset(v, 'faction', nil) |
end |
end |
end |
elseif kind == "account" then |
if name then |
db.raw.account = nil |
rawset(db, 'account', nil) |
if db.raw.namespaces then |
for name,v in pairs(db.raw.namespaces) do |
v.account = nil |
end |
end |
if db.namespaces then |
for name,v in pairs(db.namespaces) do |
rawset(v, 'account', nil) |
end |
end |
else |
if db.raw.namespaces and db.raw.namespaces[name] then |
db.raw.namespaces[name].account = nil |
end |
if db.namespaces then |
local v = db.namespaces[name] |
if v then |
rawset(v, 'account', nil) |
end |
end |
end |
elseif kind == "char" then |
if name then |
if db.charName then |
_G[db.charName] = nil |
else |
if db.raw.chars then |
db.raw.chars[charID] = nil |
end |
if db.raw.namespaces then |
for name,v in pairs(db.raw.namespaces) do |
if v.chars then |
v.chars[charID] = nil |
end |
end |
end |
end |
rawset(db, 'char', nil) |
if db.namespaces then |
for name,v in pairs(db.namespaces) do |
rawset(v, 'char', nil) |
end |
end |
else |
if db.charName then |
local x = _G[db.charName] |
if x.namespaces then |
x.namespaces[name] = nil |
end |
else |
if db.raw.namespaces then |
local v = db.namespaces[name] |
if v and v.chars then |
v.chars[charID] = nil |
end |
end |
end |
if db.namespaces then |
local v = db.namespaces[name] |
if v then |
rawset(v, 'char', nil) |
end |
end |
end |
elseif kind == "realm" then |
if not name then |
if db.raw.realms then |
db.raw.realms[realmID] = nil |
end |
rawset(db, 'realm', nil) |
if db.raw.namespaces then |
for name,v in pairs(db.raw.namespaces) do |
if v.realms then |
v.realms[realmID] = nil |
end |
end |
end |
if db.namespaces then |
for name,v in pairs(db.namespaces) do |
rawset(v, 'realm', nil) |
end |
end |
else |
if db.raw.namespaces then |
local v = db.raw.namespaces[name] |
if v and v.realms then |
v.realms[realmID] = nil |
end |
end |
if db.namespaces then |
local v = db.namespaces[name] |
if v then |
rawset(v, 'realm', nil) |
end |
end |
end |
elseif kind == "server" then |
if not name then |
if db.raw.servers then |
db.raw.servers[server] = nil |
end |
rawset(db, 'server', nil) |
if db.raw.namespaces then |
for name,v in pairs(db.raw.namespaces) do |
if v.servers then |
v.servers[server] = nil |
end |
end |
end |
if db.namespaces then |
for name,v in pairs(db.namespaces) do |
rawset(v, 'server', nil) |
end |
end |
else |
if db.raw.namespaces then |
local v = db.raw.namespaces[name] |
if v and v.servers then |
v.servers[server] = nil |
end |
end |
if db.namespaces then |
local v = db.namespaces[name] |
if v then |
rawset(v, 'server', nil) |
end |
end |
end |
elseif kind == "faction" then |
if not name then |
if db.raw.factions then |
db.raw.factions[faction] = nil |
end |
rawset(db, 'faction', nil) |
if db.raw.namespaces then |
for name,v in pairs(db.raw.namespaces) do |
if v.factions then |
v.factions[faction] = nil |
end |
end |
end |
if db.namespaces then |
for name,v in pairs(db.namespaces) do |
rawset(v, 'faction', nil) |
end |
end |
else |
if db.raw.namespaces then |
local v = db.raw.namespaces[name] |
if v and v.factions then |
v.factions[faction] = nil |
end |
end |
if db.namespaces then |
local v = db.namespaces[name] |
if v then |
rawset(v, 'faction', nil) |
end |
end |
end |
elseif kind == "class" then |
if not name then |
if db.raw.realms then |
db.raw.realms[classID] = nil |
end |
rawset(db, 'class', nil) |
if db.raw.namespaces then |
for name,v in pairs(db.raw.namespaces) do |
if v.classes then |
v.classes[classID] = nil |
end |
end |
end |
if db.namespaces then |
for name,v in pairs(db.namespaces) do |
rawset(v, 'class', nil) |
end |
end |
else |
if db.raw.namespaces then |
local v = db.raw.namespaces[name] |
if v and v.classes then |
v.classes[classID] = nil |
end |
end |
if db.namespaces then |
local v = db.namespaces[name] |
if v then |
rawset(v, 'class', nil) |
end |
end |
end |
elseif kind == "profile" then |
local id = db.raw.currentProfile and db.raw.currentProfile[charID] or AceDB.registry[self] or "Default" |
if id == "char" then |
id = "char/" .. charID |
elseif id == "class" then |
id = "class/" .. classID |
elseif id == "realm" then |
id = "realm/" .. realmID |
end |
local current = self.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedProfileDisable) == "function" then |
safecall(mixin.OnEmbedProfileDisable, mixin, self, id) |
end |
end |
end |
current = current.super |
end |
if type(self.OnProfileDisable) == "function" then |
safecall(self.OnProfileDisable, self, id) |
end |
local active = self:IsActive() |
if not name then |
if db.raw.profiles then |
db.raw.profiles[id] = nil |
end |
rawset(db, 'profile', nil) |
if db.raw.namespaces then |
for name,v in pairs(db.raw.namespaces) do |
if v.profiles then |
v.profiles[id] = nil |
end |
end |
end |
if db.namespaces then |
for name,v in pairs(db.namespaces) do |
rawset(v, 'profile', nil) |
end |
end |
else |
if db.raw.namespaces then |
local v = db.raw.namespaces[name] |
if v and v.profiles then |
v.profiles[id] = nil |
end |
end |
if db.namespaces then |
local v = db.namespaces[name] |
if v then |
rawset(v, 'profile', nil) |
end |
end |
end |
local current = self.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedProfileEnable) == "function" then |
safecall(mixin.OnEmbedProfileEnable, mixin, self, id) |
end |
end |
end |
current = current.super |
end |
if type(self.OnProfileEnable) == "function" then |
safecall(self.OnProfileEnable, self, id) |
end |
local newactive = self:IsActive() |
if active ~= newactive then |
if newactive then |
local first = nil |
if AceOO.inherits(self, "AceAddon-2.0") then |
local AceAddon = AceLibrary("AceAddon-2.0") |
if not AceAddon.addonsStarted[self] then |
return |
end |
if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[self] then |
AceAddon.addonsEnabled[self] = true |
first = true |
end |
end |
local current = self.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedEnable) == "function" then |
safecall(mixin.OnEmbedEnable, mixin, self, first) |
end |
end |
end |
current = current.super |
end |
if type(self.OnEnable) == "function" then |
safecall(self.OnEnable, self, first) |
end |
if AceEvent then |
AceEvent:TriggerEvent("Ace2_AddonEnabled", self, first) |
end |
else |
local current = self.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedDisable) == "function" then |
safecall(mixin.OnEmbedDisable, mixin, self) |
end |
end |
end |
current = current.super |
end |
if type(self.OnDisable) == "function" then |
safecall(self.OnDisable, self) |
end |
if AceEvent then |
AceEvent:TriggerEvent("Ace2_AddonDisabled", self) |
end |
end |
end |
else |
return -- skip event |
end |
if AceEvent then |
AceEvent:TriggerEvent("AceDB20_ResetDB", self, self.db.name, kind) |
end |
end |
local function cleanDefaults(t, defaults, blocker) |
if defaults then |
for k,v in pairs(t) do |
if (not blocker or (blocker[k] == nil and blocker['*'] == nil and blocker['**'] == nil)) and (defaults[k] ~= nil or defaults['*'] ~= nil or defaults['**'] ~= nil) then |
local u = defaults[k] |
if u == nil then |
u = defaults['*'] |
if u == nil then |
u = defaults['**'] |
end |
end |
if v == u then |
t[k] = nil |
elseif type(v) == "table" and type(u) == "table" then |
if cleanDefaults(v, u) then |
t[k] = nil |
else |
local w = defaults['**'] |
if w ~= u then |
if cleanDefaults(v, w, u) then |
t[k] = nil |
end |
end |
end |
end |
end |
end |
end |
return t and next(t) == nil |
end |
function AceDB:GetProfile() |
if not self.db or not self.db.raw then |
return nil |
end |
if not self.db.raw.currentProfile then |
self.db.raw.currentProfile = {} |
end |
if not self.db.raw.currentProfile[charID] then |
self.db.raw.currentProfile[charID] = AceDB.registry[self] or "Default" |
end |
local profile = self.db.raw.currentProfile[charID] |
if profile == "char" then |
return "char", "char/" .. charID |
elseif profile == "class" then |
return "class", "class/" .. classID |
elseif profile == "realm" then |
return "realm", "realm/" .. realmID |
end |
return profile, profile |
end |
local function copyTable(to, from) |
setmetatable(to, nil) |
for k,v in pairs(from) do |
if type(k) == "table" then |
k = copyTable({}, k) |
end |
if type(v) == "table" then |
v = copyTable({}, v) |
end |
to[k] = v |
end |
setmetatable(to, from) |
return to |
end |
function AceDB:SetProfile(name) |
AceDB:argCheck(name, 2, "string") |
if not self.db or not self.db.raw then |
AceDB:error("Cannot call \"SetProfile\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.") |
end |
local db = self.db |
local lowerName = name:lower() |
if lowerName:find("^char/") or lowerName:find("^realm/") or lowerName:find("^class/") then |
if lowerName:find("^char/") then |
name = "char" |
else |
name = lowerName:sub(1, 5) |
end |
lowerName = name:lower() |
end |
local oldName = db.raw.currentProfile[charID] |
if oldName:lower() == name:lower() then |
return |
end |
local oldProfileData = db.profile |
local realName = name |
if lowerName == "char" then |
realName = name .. "/" .. charID |
elseif lowerName == "realm" then |
realName = name .. "/" .. realmID |
elseif lowerName == "class" then |
realName = name .. "/" .. classID |
end |
local current = self.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedProfileDisable) == "function" then |
safecall(mixin.OnEmbedProfileDisable, mixin, self, realName) |
end |
end |
end |
current = current.super |
end |
if type(self.OnProfileDisable) == "function" then |
safecall(self.OnProfileDisable, self, realName) |
end |
local active = self:IsActive() |
db.raw.currentProfile[charID] = name |
rawset(db, 'profile', nil) |
if db.namespaces then |
for k,v in pairs(db.namespaces) do |
rawset(v, 'profile', nil) |
end |
end |
local current = self.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedProfileEnable) == "function" then |
safecall(mixin.OnEmbedProfileEnable, mixin, self, oldName, oldProfileData) |
end |
end |
end |
current = current.super |
end |
if type(self.OnProfileEnable) == "function" then |
safecall(self.OnProfileEnable, self, oldName, oldProfileData) |
end |
if cleanDefaults(oldProfileData, db.defaults and db.defaults.profile) then |
db.raw.profiles[oldName] = nil |
if not next(db.raw.profiles) then |
db.raw.profiles = nil |
end |
end |
local newactive = self:IsActive() |
if active ~= newactive then |
local first = nil |
if AceOO.inherits(self, "AceAddon-2.0") then |
local AceAddon = AceLibrary("AceAddon-2.0") |
if not AceAddon.addonsStarted[self] then |
return |
end |
if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[self] then |
first = true |
end |
end |
if newactive then |
local current = self.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedEnable) == "function" then |
safecall(mixin.OnEmbedEnable, mixin, self, first) |
end |
end |
end |
current = current.super |
end |
if type(self.OnEnable) == "function" then |
safecall(self.OnEnable, self, first) |
end |
if AceEvent then |
AceEvent:TriggerEvent("Ace2_AddonEnabled", self, first) |
end |
else |
local current = self.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedDisable) == "function" then |
safecall(mixin.OnEmbedDisable, mixin, self) |
end |
end |
end |
current = current.super |
end |
if type(self.OnDisable) == "function" then |
safecall(self.OnDisable, self) |
end |
if AceEvent then |
AceEvent:TriggerEvent("Ace2_AddonDisabled", self) |
end |
end |
end |
if self['acedb-profile-list'] then |
RecalculateAceDBProfileList(self) |
end |
if self['acedb-profile-copylist'] then |
RecalculateAceDBCopyFromList(self) |
end |
if Dewdrop then |
Dewdrop:Refresh() |
end |
end |
function AceDB:CopyProfileFrom(copyFrom) |
AceDB:argCheck(copyFrom, 2, "string") |
if not self.db or not self.db.raw then |
AceDB:error("Cannot call \"CopyProfileFrom\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.") |
end |
local db = self.db |
local lowerCopyFrom = copyFrom:lower() |
if not db.raw.profiles or not db.raw.profiles[copyFrom] then |
local good = false |
if db.raw.namespaces then |
for _,n in pairs(db.raw.namespaces) do |
if n.profiles and n.profiles[copyFrom] then |
good = true |
break |
end |
end |
end |
if not good then |
AceDB:error("Cannot copy from profile %q, it does not exist.", copyFrom) |
end |
end |
local currentProfile = db.raw.currentProfile[charID] |
if currentProfile:lower() == lowerCopyFrom then |
AceDB:error("Cannot copy from profile %q, it is currently in use.", copyFrom) |
end |
local oldProfileData = db.profile |
local current = self.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedProfileDisable) == "function" then |
safecall(mixin.OnEmbedProfileDisable, mixin, self, currentProfile) |
end |
end |
end |
current = current.super |
end |
if type(self.OnProfileDisable) == "function" then |
safecall(self.OnProfileDisable, self, realName) |
end |
local active = self:IsActive() |
for k,v in pairs(db.profile) do |
db.profile[k] = nil |
end |
if db.raw.profiles[copyFrom] then |
copyTable(db.profile, db.raw.profiles[copyFrom]) |
end |
inheritDefaults(db.profile, db.defaults and db.defaults.profile) |
if db.namespaces then |
for l,u in pairs(db.namespaces) do |
for k,v in pairs(u.profile) do |
u.profile[k] = nil |
end |
if db.raw.namespaces[l].profiles[copyFrom] then |
copyTable(u.profile, db.raw.namespaces[l].profiles[copyFrom]) |
end |
inheritDefaults(u.profile, u.defaults and u.defaults.profile) |
end |
end |
local current = self.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedProfileEnable) == "function" then |
safecall(mixin.OnEmbedProfileEnable, mixin, self, copyFrom, oldProfileData, copyFrom) |
end |
end |
end |
current = current.super |
end |
if type(self.OnProfileEnable) == "function" then |
safecall(self.OnProfileEnable, self, copyFrom, oldProfileData, copyFrom) |
end |
local newactive = self:IsActive() |
if active ~= newactive then |
if AceOO.inherits(self, "AceAddon-2.0") then |
local AceAddon = AceLibrary("AceAddon-2.0") |
if not AceAddon.addonsStarted[self] then |
return |
end |
end |
if newactive then |
local current = self.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedEnable) == "function" then |
safecall(mixin.OnEmbedEnable, mixin, self) |
end |
end |
end |
current = current.super |
end |
if type(self.OnEnable) == "function" then |
safecall(self.OnEnable, self) |
end |
if AceEvent then |
AceEvent:TriggerEvent("Ace2_AddonEnabled", self) |
end |
else |
local current = self.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedDisable) == "function" then |
safecall(mixin.OnEmbedDisable, mixin, self) |
end |
end |
end |
current = current.super |
end |
if type(self.OnDisable) == "function" then |
safecall(self.OnDisable, self) |
end |
if AceEvent then |
AceEvent:TriggerEvent("Ace2_AddonDisabled", self) |
end |
end |
end |
if self['acedb-profile-list'] then |
RecalculateAceDBProfileList(self) |
end |
if self['acedb-profile-copylist'] then |
RecalculateAceDBCopyFromList(self) |
end |
if Dewdrop then |
Dewdrop:Refresh() |
end |
end |
function AceDB:DeleteProfile(profile, noconfirm) |
AceDB:argCheck(profile , 2, "string") |
if not self.db or not self.db.raw then |
AceDB:error("Cannot call \"DeleteProfile\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.") |
end |
local db = self.db |
local currentProfile = db.raw.currentProfile[charID] |
if currentProfile:lower() == profile:lower() then |
AceDB:error("Cannot delete profile %q, it is currently in use.", profile) |
end |
if not (noconfirm or IsShiftKeyDown()) then |
if not StaticPopupDialogs["ACEDB20_CONFIRM_DELETE_DIALOG"] then |
StaticPopupDialogs["ACEDB20_CONFIRM_DELETE_DIALOG"] = {} |
end |
local t = StaticPopupDialogs["ACEDB20_CONFIRM_DELETE_DIALOG"] |
t.text = format("%s: %s?", DELETE_PROFILE, profile) |
t.button1 = DELETE_PROFILE |
t.button2 = CANCEL or "Cancel" |
t.OnAccept = function() |
self:DeleteProfile(profile, true) |
end |
t.timeout = 0 |
t.whileDead = 1 |
t.hideOnEscape = 1 |
StaticPopup_Show("ACEDB20_CONFIRM_DELETE_DIALOG") |
return; |
end |
local good = false |
if db.raw.profiles and db.raw.profiles[profile] then |
good = true; |
db.raw.profiles[profile] = nil; |
end |
if db.raw.namespaces then |
for _,n in pairs(db.raw.namespaces) do |
if n.profiles and n.profiles[profile] then |
n.profiles[profile] = nil; |
good = true |
end |
end |
end |
if not good then |
AceDB:error("Cannot delete profile %q, it does not exist.", profile) |
end |
if self['acedb-profile-list'] then |
RecalculateAceDBProfileList(self) |
end |
if self['acedb-profile-copylist'] then |
RecalculateAceDBCopyFromList(self) |
end |
if Dewdrop then |
Dewdrop:Refresh() |
end |
end |
function AceDB:IsActive() |
return not self.db or not self.db.raw or not self.db.raw.disabled or not self.db.raw.disabled[self.db.raw.currentProfile[charID]] |
end |
function AceDB:ToggleActive(state) |
AceDB:argCheck(state, 2, "boolean", "nil") |
if not self.db or not self.db.raw then |
AceDB:error("Cannot call \"ToggleActive\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.") |
end |
local db = self.db |
if not db.raw.disabled then |
db.raw.disabled = setmetatable({}, caseInsensitive_mt) |
end |
local profile = db.raw.currentProfile[charID] |
local disable |
if state == nil then |
disable = not db.raw.disabled[profile] |
else |
disable = not state |
if disable == db.raw.disabled[profile] then |
return |
end |
end |
db.raw.disabled[profile] = disable or nil |
if AceOO.inherits(self, "AceAddon-2.0") then |
local AceAddon = AceLibrary("AceAddon-2.0") |
if not AceAddon.addonsStarted[self] then |
return |
end |
end |
if not disable then |
local current = self.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedEnable) == "function" then |
safecall(mixin.OnEmbedEnable, mixin, self) |
end |
end |
end |
current = current.super |
end |
if type(self.OnEnable) == "function" then |
safecall(self.OnEnable, self) |
end |
if AceEvent then |
AceEvent:TriggerEvent("Ace2_AddonEnabled", self) |
end |
else |
local current = self.class |
while current and current ~= AceOO.Class do |
if current.mixins then |
for mixin in pairs(current.mixins) do |
if type(mixin.OnEmbedDisable) == "function" then |
safecall(mixin.OnEmbedDisable, mixin, self) |
end |
end |
end |
current = current.super |
end |
if type(self.OnDisable) == "function" then |
safecall(self.OnDisable, self) |
end |
if AceEvent then |
AceEvent:TriggerEvent("Ace2_AddonDisabled", self) |
end |
end |
return not disable |
end |
function AceDB:embed(target) |
self.super.embed(self, target) |
if not AceEvent then |
AceDB:error(MAJOR_VERSION .. " requires AceEvent-2.0") |
end |
end |
function AceDB:ADDON_LOADED(name) |
AceDB.addonsLoaded[name] = true |
for addon, addonName in pairs(AceDB.addonsToBeInitialized) do |
if name == addonName then |
AceDB.InitializeDB(addon, name) |
AceDB.addonsToBeInitialized[addon] = nil |
end |
end |
end |
function AceDB:PLAYER_LOGOUT() |
for addon, defaultProfile in pairs(AceDB.registry) do |
local db = addon.db |
if db then |
if type(addon.OnDatabaseCleanup) == "function" then |
safecall(addon.OnDatabaseCleanup, addon) |
end |
setmetatable(db, nil) |
CrawlForSerialization(db.raw) |
if type(_G[db.charName]) == "table" then |
CrawlForSerialization(_G[db.charName]) |
end |
if db.char and cleanDefaults(db.char, db.defaults and db.defaults.char) then |
if db.charName and _G[db.charName] and _G[db.charName].global == db.char then |
_G[db.charName].global = nil |
if not next(_G[db.charName]) then |
_G[db.charName] = nil |
end |
else |
if db.raw.chars then |
db.raw.chars[charID] = nil |
if not next(db.raw.chars) then |
db.raw.chars = nil |
end |
end |
end |
end |
if db.realm and cleanDefaults(db.realm, db.defaults and db.defaults.realm) then |
if db.raw.realms then |
db.raw.realms[realmID] = nil |
if not next(db.raw.realms) then |
db.raw.realms = nil |
end |
end |
end |
if db.server and cleanDefaults(db.server, db.defaults and db.defaults.server) then |
if db.raw.servers then |
db.raw.servers[server] = nil |
if not next(db.raw.servers) then |
db.raw.servers = nil |
end |
end |
end |
if db.faction and cleanDefaults(db.faction, db.defaults and db.defaults.faction) then |
if db.raw.factions then |
db.raw.factions[faction] = nil |
if not next(db.raw.factions) then |
db.raw.factions = nil |
end |
end |
end |
if db.class and cleanDefaults(db.class, db.defaults and db.defaults.class) then |
if db.raw.classes then |
db.raw.classes[classID] = nil |
if not next(db.raw.classes) then |
db.raw.classes = nil |
end |
end |
end |
if db.account and cleanDefaults(db.account, db.defaults and db.defaults.account) then |
db.raw.account = nil |
end |
if db.profile and cleanDefaults(db.profile, db.defaults and db.defaults.profile) then |
if db.raw.profiles then |
db.raw.profiles[db.raw.currentProfile and db.raw.currentProfile[charID] or defaultProfile or "Default"] = nil |
if not next(db.raw.profiles) then |
db.raw.profiles = nil |
end |
end |
end |
if db.namespaces and db.raw.namespaces then |
for name,v in pairs(db.namespaces) do |
if db.raw.namespaces[name] then |
setmetatable(v, nil) |
if v.char and cleanDefaults(v.char, v.defaults and v.defaults.char) then |
if db.charName and _G[db.charName] and _G[db.charName].namespaces and _G[db.charName].namespaces[name] == v then |
_G[db.charName].namespaces[name] = nil |
if not next(_G[db.charName].namespaces) then |
_G[db.charName].namespaces = nil |
if not next(_G[db.charName]) then |
_G[db.charName] = nil |
end |
end |
else |
if db.raw.namespaces[name].chars then |
db.raw.namespaces[name].chars[charID] = nil |
if not next(db.raw.namespaces[name].chars) then |
db.raw.namespaces[name].chars = nil |
end |
end |
end |
end |
if v.realm and cleanDefaults(v.realm, v.defaults and v.defaults.realm) then |
if db.raw.namespaces[name].realms then |
db.raw.namespaces[name].realms[realmID] = nil |
if not next(db.raw.namespaces[name].realms) then |
db.raw.namespaces[name].realms = nil |
end |
end |
end |
if v.server and cleanDefaults(v.server, v.defaults and v.defaults.server) then |
if db.raw.namespaces[name].servers then |
db.raw.namespaces[name].servers[server] = nil |
if not next(db.raw.namespaces[name].servers) then |
db.raw.namespaces[name].servers = nil |
end |
end |
end |
if v.faction and cleanDefaults(v.faction, v.defaults and v.defaults.faction) then |
if db.raw.namespaces[name].factions then |
db.raw.namespaces[name].factions[faction] = nil |
if not next(db.raw.namespaces[name].factions) then |
db.raw.namespaces[name].factions = nil |
end |
end |
end |
if v.class and cleanDefaults(v.class, v.defaults and v.defaults.class) then |
if db.raw.namespaces[name].classes then |
db.raw.namespaces[name].classes[classID] = nil |
if not next(db.raw.namespaces[name].classes) then |
db.raw.namespaces[name].classes = nil |
end |
end |
end |
if v.account and cleanDefaults(v.account, v.defaults and v.defaults.account) then |
db.raw.namespaces[name].account = nil |
end |
if v.profile and cleanDefaults(v.profile, v.defaults and v.defaults.profile) then |
if db.raw.namespaces[name].profiles then |
db.raw.namespaces[name].profiles[db.raw.currentProfile and db.raw.currentProfile[charID] or defaultProfile or "Default"] = nil |
if not next(db.raw.namespaces[name].profiles) then |
db.raw.namespaces[name].profiles = nil |
end |
end |
end |
if not next(db.raw.namespaces[name]) then |
db.raw.namespaces[name] = nil |
end |
end |
end |
if not next(db.raw.namespaces) then |
db.raw.namespaces = nil |
end |
end |
if db.raw.disabled and not next(db.raw.disabled) then |
db.raw.disabled = nil |
end |
if db.raw.currentProfile then |
for k,v in pairs(db.raw.currentProfile) do |
if v:lower() == (defaultProfile or "Default"):lower() then |
db.raw.currentProfile[k] = nil |
end |
end |
if not next(db.raw.currentProfile) then |
db.raw.currentProfile = nil |
end |
end |
if _G[db.name] and not next(_G[db.name]) then |
_G[db.name] = nil |
end |
end |
end |
end |
function AceDB:AcquireDBNamespace(name) |
AceDB:argCheck(name, 2, "string") |
local db = self.db |
if not db then |
AceDB:error("Cannot call `AcquireDBNamespace' before `RegisterDB' has been called.", 2) |
end |
if not db.namespaces then |
rawset(db, 'namespaces', {}) |
end |
if not db.namespaces[name] then |
local namespace = {} |
db.namespaces[name] = namespace |
namespace.db = db |
namespace.name = name |
setmetatable(namespace, namespace_mt) |
end |
return db.namespaces[name] |
end |
function AceDB:GetAceOptionsDataTable(target) |
if not target['acedb-profile-list'] then |
target['acedb-profile-list'] = setmetatable({}, caseInsensitive_mt) |
RecalculateAceDBProfileList(target) |
end |
if not target['acedb-profile-copylist'] then |
target['acedb-profile-copylist'] = setmetatable({}, caseInsensitive_mt) |
RecalculateAceDBCopyFromList(target) |
end |
return { |
standby = { |
cmdName = STATE, |
guiName = ENABLED, |
name = ACTIVE, |
desc = TOGGLE_ACTIVE, |
type = "toggle", |
get = "IsActive", |
set = "ToggleActive", |
map = MAP_ACTIVESUSPENDED, |
order = -3, |
}, |
profile = { |
type = 'group', |
name = PROFILE, |
desc = SET_PROFILE, |
order = -3.5, |
get = "GetProfile", |
args = { |
choose = { |
guiName = CHOOSE_PROFILE_GUI, |
cmdName = PROFILE, |
desc = CHOOSE_PROFILE_DESC, |
type = 'text', |
get = "GetProfile", |
set = "SetProfile", |
validate = target['acedb-profile-list'] |
}, |
copy = { |
guiName = COPY_PROFILE_GUI, |
cmdName = PROFILE, |
desc = COPY_PROFILE_DESC, |
type = 'text', |
get = false, |
set = "CopyProfileFrom", |
validate = target['acedb-profile-copylist'], |
disabled = function() |
return not next(target['acedb-profile-copylist']) |
end, |
}, |
other = { |
guiName = OTHER_PROFILE_GUI, |
cmdName = PROFILE, |
desc = OTHER_PROFILE_DESC, |
usage = OTHER_PROFILE_USAGE, |
type = 'text', |
get = "GetProfile", |
set = "SetProfile", |
}, |
delete = { |
name = DELETE_PROFILE, |
desc = DELETE_PROFILE_DESC, |
usage = DELETE_PROFILE_USAGE, |
type = 'text', |
set = "DeleteProfile", |
get = false, |
validate = target['acedb-profile-copylist'], |
disabled = function() |
return not next(target['acedb-profile-copylist']) |
end, |
}, |
reset = { |
name = RESET_PROFILE, |
desc = RESET_PROFILE_DESC, |
type = 'execute', |
func = function() |
target:ResetDB('profile') |
end, |
confirm = true, |
} |
} |
}, |
} |
end |
local function activate(self, oldLib, oldDeactivate) |
AceDB = self |
AceEvent = AceLibrary:HasInstance("AceEvent-2.0") and AceLibrary("AceEvent-2.0") |
self.addonsToBeInitialized = oldLib and oldLib.addonsToBeInitialized or {} |
self.addonsLoaded = oldLib and oldLib.addonsLoaded or {} |
self.registry = oldLib and oldLib.registry or {} |
for k, v in pairs(self.registry) do |
if v == true then |
self.registry[k] = "Default" |
end |
end |
self:activate(oldLib, oldDeactivate) |
for t in pairs(self.embedList) do |
if t.db then |
rawset(t.db, 'char', nil) |
rawset(t.db, 'realm', nil) |
rawset(t.db, 'class', nil) |
rawset(t.db, 'account', nil) |
rawset(t.db, 'server', nil) |
rawset(t.db, 'faction', nil) |
rawset(t.db, 'profile', nil) |
setmetatable(t.db, db_mt) |
end |
end |
if oldLib then |
oldDeactivate(oldLib) |
end |
end |
local function external(self, major, instance) |
if major == "AceEvent-2.0" then |
AceEvent = instance |
AceEvent:embed(self) |
self:RegisterEvent("ADDON_LOADED") |
self:RegisterEvent("PLAYER_LOGOUT") |
elseif major == "Dewdrop-2.0" then |
Dewdrop = instance |
end |
end |
AceLibrary:Register(AceDB, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) |
AceDB = AceLibrary(MAJOR_VERSION) |
## Interface: 20400 |
## Title: Lib: AceLibrary |
## Notes: AddOn development framework |
## Author: Ace Development Team |
## X-Website: http://www.wowace.com |
## X-Category: Library |
## X-License: LGPL v2.1 + MIT for AceOO-2.0 |
AceLibrary.lua |
--[[ |
Name: AceLibrary |
Revision: $Rev: 58127 $ |
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) |
Inspired By: Iriel (iriel@vigilance-committee.org) |
Tekkub (tekkub@gmail.com) |
Revision: $Rev: 58127 $ |
Website: http://www.wowace.com/ |
Documentation: http://www.wowace.com/index.php/AceLibrary |
SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary |
Description: Versioning library to handle other library instances, upgrading, |
and proper access. |
It also provides a base for libraries to work off of, providing |
proper error tools. It is handy because all the errors occur in the |
file that called it, not in the library file itself. |
Dependencies: None |
License: LGPL v2.1 |
]] |
local ACELIBRARY_MAJOR = "AceLibrary" |
local ACELIBRARY_MINOR = "$Revision: 58127 $" |
local _G = getfenv(0) |
local previous = _G[ACELIBRARY_MAJOR] |
if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end |
do |
-- 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 |
end |
local LibStub = _G.LibStub |
-- If you don't want AceLibrary to enable libraries that are LoadOnDemand but |
-- disabled in the addon screen, set this to true. |
local DONT_ENABLE_LIBRARIES = nil |
local function safecall(func,...) |
local success, err = pcall(func,...) |
if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("\n(.-: )in.-\n") or "") .. err) end |
end |
-- @table AceLibrary |
-- @brief System to handle all versioning of libraries. |
local AceLibrary = {} |
local AceLibrary_mt = {} |
setmetatable(AceLibrary, AceLibrary_mt) |
local function error(self, message, ...) |
if type(self) ~= "table" then |
return _G.error(("Bad argument #1 to `error' (table expected, got %s)"):format(type(self)), 2) |
end |
local stack = debugstack() |
if not message then |
local second = stack:match("\n(.-)\n") |
message = "error raised! " .. second |
else |
local arg = { ... } -- not worried about table creation, as errors don't happen often |
for i = 1, #arg do |
arg[i] = tostring(arg[i]) |
end |
for i = 1, 10 do |
table.insert(arg, "nil") |
end |
message = message:format(unpack(arg)) |
end |
if getmetatable(self) and getmetatable(self).__tostring then |
message = ("%s: %s"):format(tostring(self), message) |
elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then |
message = ("%s: %s"):format(self:GetLibraryVersion(), message) |
elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then |
message = ("%s: %s"):format(self.class:GetLibraryVersion(), message) |
end |
local first = stack:gsub("\n.*", "") |
local file = first:gsub(".*\\(.*).lua:%d+: .*", "%1") |
file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") |
local i = 0 |
for s in stack:gmatch("\n([^\n]*)") do |
i = i + 1 |
if not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then |
file = s:gsub("^.*\\(.*).lua:%d+: .*", "%1") |
file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") |
break |
end |
end |
local j = 0 |
for s in stack:gmatch("\n([^\n]*)") do |
j = j + 1 |
if j > i and not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then |
return _G.error(message, j+1) |
end |
end |
return _G.error(message, 2) |
end |
local type = type |
local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5) |
if type(num) ~= "number" then |
return error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num)) |
elseif type(kind) ~= "string" then |
return error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind)) |
end |
arg = type(arg) |
if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then |
local stack = debugstack() |
local func = stack:match("`argCheck'.-([`<].-['>])") |
if not func then |
func = stack:match("([`<].-['>])") |
end |
if kind5 then |
return error(self, "Bad argument #%s to %s (%s, %s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, kind5, arg) |
elseif kind4 then |
return error(self, "Bad argument #%s to %s (%s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, arg) |
elseif kind3 then |
return error(self, "Bad argument #%s to %s (%s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, arg) |
elseif kind2 then |
return error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg) |
else |
return error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg) |
end |
end |
end |
local pcall |
do |
local function check(self, ret, ...) |
if not ret then |
local s = ... |
return error(self, (s:gsub(".-%.lua:%d-: ", ""))) |
else |
return ... |
end |
end |
function pcall(self, func, ...) |
return check(self, _G.pcall(func, ...)) |
end |
end |
local recurse = {} |
local function addToPositions(t, major) |
if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then |
rawset(t, recurse, true) |
AceLibrary.positions[t] = major |
for k,v in pairs(t) do |
if type(v) == "table" and not rawget(v, recurse) then |
addToPositions(v, major) |
end |
if type(k) == "table" and not rawget(k, recurse) then |
addToPositions(k, major) |
end |
end |
local mt = getmetatable(t) |
if mt and not rawget(mt, recurse) then |
addToPositions(mt, major) |
end |
rawset(t, recurse, nil) |
end |
end |
local function svnRevisionToNumber(text) |
local kind = type(text) |
if kind == "number" or tonumber(text) then |
return tonumber(text) |
elseif kind == "string" then |
if text:find("^%$Revision: (%d+) %$$") then |
return tonumber((text:match("^%$Revision: (%d+) %$$"))) |
elseif text:find("^%$Rev: (%d+) %$$") then |
return tonumber((text:match("^%$Rev: (%d+) %$$"))) |
elseif text:find("^%$LastChangedRevision: (%d+) %$$") then |
return tonumber((text:match("^%$LastChangedRevision: (%d+) %$$"))) |
end |
end |
return nil |
end |
local crawlReplace |
do |
local recurse = {} |
local function func(t, to, from) |
if recurse[t] then |
return |
end |
recurse[t] = true |
local mt = getmetatable(t) |
setmetatable(t, nil) |
rawset(t, to, rawget(t, from)) |
rawset(t, from, nil) |
for k,v in pairs(t) do |
if v == from then |
t[k] = to |
elseif type(v) == "table" then |
if not recurse[v] then |
func(v, to, from) |
end |
end |
if type(k) == "table" then |
if not recurse[k] then |
func(k, to, from) |
end |
end |
end |
setmetatable(t, mt) |
if mt then |
if mt == from then |
setmetatable(t, to) |
elseif not recurse[mt] then |
func(mt, to, from) |
end |
end |
end |
function crawlReplace(t, to, from) |
func(t, to, from) |
for k in pairs(recurse) do |
recurse[k] = nil |
end |
end |
end |
-- @function destroyTable |
-- @brief remove all the contents of a table |
-- @param t table to destroy |
local function destroyTable(t) |
setmetatable(t, nil) |
for k,v in pairs(t) do |
t[k] = nil |
end |
end |
local function isFrame(frame) |
return type(frame) == "table" and type(rawget(frame, 0)) == "userdata" and type(rawget(frame, 'IsFrameType')) == "function" and getmetatable(frame) and type(rawget(getmetatable(frame), '__index')) == "function" |
end |
-- @function copyTable |
-- @brief Create a shallow copy of a table and return it. |
-- @param from The table to copy from |
-- @return A shallow copy of the table |
local function copyTable(from, to) |
if not to then |
to = {} |
end |
for k,v in pairs(from) do |
to[k] = v |
end |
setmetatable(to, getmetatable(from)) |
return to |
end |
-- @function deepTransfer |
-- @brief Fully transfer all data, keeping proper previous table |
-- backreferences stable. |
-- @param to The table with which data is to be injected into |
-- @param from The table whose data will be injected into the first |
-- @param saveFields If available, a shallow copy of the basic data is saved |
-- in here. |
-- @param list The account of table references |
-- @param list2 The current status on which tables have been traversed. |
local deepTransfer |
do |
-- @function examine |
-- @brief Take account of all the table references to be shared |
-- between the to and from tables. |
-- @param to The table with which data is to be injected into |
-- @param from The table whose data will be injected into the first |
-- @param list An account of the table references |
local function examine(to, from, list, major) |
list[from] = to |
for k,v in pairs(from) do |
if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then |
if from[k] == to[k] then |
list[from[k]] = to[k] |
elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then |
list[from[k]] = from[k] |
elseif not list[from[k]] then |
examine(to[k], from[k], list, major) |
end |
end |
end |
return list |
end |
function deepTransfer(to, from, saveFields, major, list, list2) |
setmetatable(to, nil) |
if not list then |
list = {} |
list2 = {} |
examine(to, from, list, major) |
end |
list2[to] = to |
for k,v in pairs(to) do |
if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then |
if saveFields then |
saveFields[k] = v |
end |
to[k] = nil |
elseif v ~= _G then |
if saveFields then |
saveFields[k] = copyTable(v) |
end |
end |
end |
for k in pairs(from) do |
if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then |
if not list2[to[k]] then |
deepTransfer(to[k], from[k], nil, major, list, list2) |
end |
to[k] = list[to[k]] or list2[to[k]] |
else |
rawset(to, k, from[k]) |
end |
end |
setmetatable(to, getmetatable(from)) |
local mt = getmetatable(to) |
if mt then |
if list[mt] then |
setmetatable(to, list[mt]) |
elseif mt.__index and list[mt.__index] then |
mt.__index = list[mt.__index] |
end |
end |
destroyTable(from) |
end |
end |
local function TryToEnable(addon) |
if DONT_ENABLE_LIBRARIES then return end |
local isondemand = IsAddOnLoadOnDemand(addon) |
if isondemand then |
local _, _, _, enabled = GetAddOnInfo(addon) |
EnableAddOn(addon) |
local _, _, _, _, loadable = GetAddOnInfo(addon) |
if not loadable and not enabled then |
DisableAddOn(addon) |
end |
return loadable |
end |
end |
-- @method TryToLoadStandalone |
-- @brief Attempt to find and load a standalone version of the requested library |
-- @param major A string representing the major version |
-- @return If library is found and loaded, true is return. If not loadable, false is returned. |
-- If the library has been requested previously, nil is returned. |
local function TryToLoadStandalone(major) |
if not AceLibrary.scannedlibs then AceLibrary.scannedlibs = {} end |
if AceLibrary.scannedlibs[major] then return end |
AceLibrary.scannedlibs[major] = true |
local name, _, _, enabled, loadable = GetAddOnInfo(major) |
loadable = (enabled and loadable) or TryToEnable(name) |
local loaded = false |
if loadable then |
loaded = true |
LoadAddOn(name) |
end |
local field = "X-AceLibrary-" .. major |
for i = 1, GetNumAddOns() do |
if GetAddOnMetadata(i, field) then |
name, _, _, enabled, loadable = GetAddOnInfo(i) |
loadable = (enabled and loadable) or TryToEnable(name) |
if loadable then |
loaded = true |
LoadAddOn(name) |
end |
end |
end |
return loaded |
end |
-- @method IsNewVersion |
-- @brief Obtain whether the supplied version would be an upgrade to the |
-- current version. This allows for bypass code in library |
-- declaration. |
-- @param major A string representing the major version |
-- @param minor An integer or an svn revision string representing the minor version |
-- @return whether the supplied version would be newer than what is |
-- currently available. |
function AceLibrary:IsNewVersion(major, minor) |
argCheck(self, major, 2, "string") |
TryToLoadStandalone(major) |
if type(minor) == "string" then |
local m = svnRevisionToNumber(minor) |
if m then |
minor = m |
else |
_G.error(("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) |
end |
end |
argCheck(self, minor, 3, "number") |
local lib, oldMinor = LibStub:GetLibrary(major, true) |
if lib then |
return oldMinor < minor |
end |
local data = self.libs[major] |
if not data then |
return true |
end |
return data.minor < minor |
end |
-- @method HasInstance |
-- @brief Returns whether an instance exists. This allows for optional support of a library. |
-- @param major A string representing the major version. |
-- @param minor (optional) An integer or an svn revision string representing the minor version. |
-- @return Whether an instance exists. |
function AceLibrary:HasInstance(major, minor) |
argCheck(self, major, 2, "string") |
if minor ~= false then |
TryToLoadStandalone(major) |
end |
local lib, ver = LibStub:GetLibrary(major, true) |
if not lib and self.libs[major] then |
lib, ver = self.libs[major].instance, self.libs[major].minor |
end |
if minor then |
if type(minor) == "string" then |
local m = svnRevisionToNumber(minor) |
if m then |
minor = m |
else |
_G.error(("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) |
end |
end |
argCheck(self, minor, 3, "number") |
if not lib then |
return false |
end |
return ver == minor |
end |
return not not lib |
end |
-- @method GetInstance |
-- @brief Returns the library with the given major/minor version. |
-- @param major A string representing the major version. |
-- @param minor (optional) An integer or an svn revision string representing the minor version. |
-- @return The library with the given major/minor version. |
function AceLibrary:GetInstance(major, minor) |
argCheck(self, major, 2, "string") |
if minor ~= false then |
TryToLoadStandalone(major) |
end |
local data, ver = LibStub:GetLibrary(major, true) |
if not data then |
if self.libs[major] then |
data, ver = self.libs[major].instance, self.libs[major].minor |
else |
_G.error(("Cannot find a library instance of %s."):format(major), 2) |
return |
end |
end |
if minor then |
if type(minor) == "string" then |
local m = svnRevisionToNumber(minor) |
if m then |
minor = m |
else |
_G.error(("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) |
end |
end |
argCheck(self, minor, 2, "number") |
if ver ~= minor then |
_G.error(("Cannot find a library instance of %s, minor version %d."):format(major, minor), 2) |
end |
end |
return data |
end |
-- Syntax sugar. AceLibrary("FooBar-1.0") |
AceLibrary_mt.__call = AceLibrary.GetInstance |
local donothing = function() end |
local AceEvent |
local tmp = {} |
-- @method Register |
-- @brief Registers a new version of a given library. |
-- @param newInstance the library to register |
-- @param major the major version of the library |
-- @param minor the minor version of the library |
-- @param activateFunc (optional) A function to be called when the library is |
-- fully activated. Takes the arguments |
-- (newInstance [, oldInstance, oldDeactivateFunc]). If |
-- oldInstance is given, you should probably call |
-- oldDeactivateFunc(oldInstance). |
-- @param deactivateFunc (optional) A function to be called by a newer library's |
-- activateFunc. |
-- @param externalFunc (optional) A function to be called whenever a new |
-- library is registered. |
function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc) |
argCheck(self, newInstance, 2, "table") |
argCheck(self, major, 3, "string") |
if major ~= ACELIBRARY_MAJOR then |
for k,v in pairs(_G) do |
if v == newInstance then |
geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k)) |
end |
end |
end |
if major ~= ACELIBRARY_MAJOR and not major:find("^[%a%-][%a%d%-]*%-%d+%.%d+$") then |
_G.error(string.format("Bad argument #3 to `Register'. Must be in the form of \"Name-1.0\". %q is not appropriate", major), 2) |
end |
if type(minor) == "string" then |
local m = svnRevisionToNumber(minor) |
if m then |
minor = m |
else |
_G.error(("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) |
end |
end |
argCheck(self, minor, 4, "number") |
if math.floor(minor) ~= minor or minor < 0 then |
error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor) |
end |
argCheck(self, activateFunc, 5, "function", "nil") |
argCheck(self, deactivateFunc, 6, "function", "nil") |
argCheck(self, externalFunc, 7, "function", "nil") |
if not deactivateFunc then |
deactivateFunc = donothing |
end |
local data = self.libs[major] |
if not data then |
-- This is new |
if LibStub:GetLibrary(major, true) then |
error(self, "Cannot register library %q. It is already registered with LibStub.", major) |
end |
local instance = LibStub:NewLibrary(major, minor) |
copyTable(newInstance, instance) |
crawlReplace(instance, instance, newInstance) |
destroyTable(newInstance) |
if AceLibrary == newInstance then |
self = instance |
AceLibrary = instance |
end |
self.libs[major] = { |
instance = instance, |
minor = minor, |
deactivateFunc = deactivateFunc, |
externalFunc = externalFunc, |
} |
rawset(instance, 'GetLibraryVersion', function(self) |
return major, minor |
end) |
if not rawget(instance, 'error') then |
rawset(instance, 'error', error) |
end |
if not rawget(instance, 'argCheck') then |
rawset(instance, 'argCheck', argCheck) |
end |
if not rawget(instance, 'pcall') then |
rawset(instance, 'pcall', pcall) |
end |
addToPositions(instance, major) |
if activateFunc then |
safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil |
end |
if externalFunc then |
for k, data_instance in LibStub:IterateLibraries() do -- all libraries |
tmp[k] = data_instance |
end |
for k, data in pairs(self.libs) do -- Ace libraries which may not have been registered with LibStub |
tmp[k] = data.instance |
end |
for k, data_instance in pairs(tmp) do |
if k ~= major then |
safecall(externalFunc, instance, k, data_instance) |
end |
tmp[k] = nil |
end |
end |
for k,data in pairs(self.libs) do -- only Ace libraries |
if k ~= major and data.externalFunc then |
safecall(data.externalFunc, data.instance, major, instance) |
end |
end |
if major == "AceEvent-2.0" then |
AceEvent = instance |
end |
if AceEvent then |
AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance) |
end |
return instance |
end |
if minor <= data.minor then |
-- This one is already obsolete, raise an error. |
_G.error(("Obsolete library registered. %s is already registered at version %d. You are trying to register version %d. Hint: if not AceLibrary:IsNewVersion(%q, %d) then return end"):format(major, data.minor, minor, major, minor), 2) |
return |
end |
local instance = data.instance |
-- This is an update |
local oldInstance = {} |
local libStubInstance = LibStub:GetLibrary(major, true) |
if not libStubInstance then -- non-LibStub AceLibrary registered the library |
-- pass |
elseif libStubInstance ~= instance then |
error(self, "Cannot register library %q. It is already registered with LibStub.", major) |
else |
LibStub:NewLibrary(major, minor) -- upgrade the minor version |
end |
addToPositions(newInstance, major) |
local isAceLibrary = (AceLibrary == newInstance) |
local old_error, old_argCheck, old_pcall |
if isAceLibrary then |
self = instance |
AceLibrary = instance |
old_error = instance.error |
old_argCheck = instance.argCheck |
old_pcall = instance.pcall |
self.error = error |
self.argCheck = argCheck |
self.pcall = pcall |
end |
deepTransfer(instance, newInstance, oldInstance, major) |
crawlReplace(instance, instance, newInstance) |
local oldDeactivateFunc = data.deactivateFunc |
data.minor = minor |
data.deactivateFunc = deactivateFunc |
data.externalFunc = externalFunc |
rawset(instance, 'GetLibraryVersion', function() |
return major, minor |
end) |
if not rawget(instance, 'error') then |
rawset(instance, 'error', error) |
end |
if not rawget(instance, 'argCheck') then |
rawset(instance, 'argCheck', argCheck) |
end |
if not rawget(instance, 'pcall') then |
rawset(instance, 'pcall', pcall) |
end |
if isAceLibrary then |
for _,v in pairs(self.libs) do |
local i = type(v) == "table" and v.instance |
if type(i) == "table" then |
if not rawget(i, 'error') or i.error == old_error then |
rawset(i, 'error', error) |
end |
if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then |
rawset(i, 'argCheck', argCheck) |
end |
if not rawget(i, 'pcall') or i.pcall == old_pcall then |
rawset(i, 'pcall', pcall) |
end |
end |
end |
end |
if activateFunc then |
safecall(activateFunc, instance, oldInstance, oldDeactivateFunc) |
else |
safecall(oldDeactivateFunc, oldInstance) |
end |
oldInstance = nil |
if externalFunc then |
for k, data_instance in LibStub:IterateLibraries() do -- all libraries |
tmp[k] = data_instance |
end |
for k, data in pairs(self.libs) do -- Ace libraries which may not have been registered with LibStub |
tmp[k] = data.instance |
end |
for k, data_instance in pairs(tmp) do |
if k ~= major then |
safecall(externalFunc, instance, k, data_instance) |
end |
tmp[k] = nil |
end |
end |
return instance |
end |
function AceLibrary:IterateLibraries() |
local t = {} |
for major, instance in LibStub:IterateLibraries() do |
t[major] = instance |
end |
for major, data in pairs(self.libs) do |
t[major] = data.instance |
end |
return pairs(t) |
end |
local function manuallyFinalize(major, instance) |
if AceLibrary.libs[major] then |
-- don't work on Ace libraries |
return |
end |
local finalizedExternalLibs = AceLibrary.finalizedExternalLibs |
if finalizedExternalLibs[major] then |
return |
end |
finalizedExternalLibs[major] = true |
for k,data in pairs(AceLibrary.libs) do -- only Ace libraries |
if k ~= major and data.externalFunc then |
safecall(data.externalFunc, data.instance, major, instance) |
end |
end |
end |
-- @function Activate |
-- @brief The activateFunc for AceLibrary itself. Called when |
-- AceLibrary properly registers. |
-- @param self Reference to AceLibrary |
-- @param oldLib (optional) Reference to an old version of AceLibrary |
-- @param oldDeactivate (optional) Function to deactivate the old lib |
local function activate(self, oldLib, oldDeactivate) |
AceLibrary = self |
if not self.libs then |
self.libs = oldLib and oldLib.libs or {} |
self.scannedlibs = oldLib and oldLib.scannedlibs or {} |
end |
if not self.positions then |
self.positions = oldLib and oldLib.positions or setmetatable({}, { __mode = "k" }) |
end |
self.finalizedExternalLibs = oldLib and oldLib.finalizedExternalLibs or {} |
self.frame = oldLib and oldLib.frame or CreateFrame("Frame") |
self.frame:UnregisterAllEvents() |
self.frame:RegisterEvent("ADDON_LOADED") |
self.frame:SetScript("OnEvent", function() |
for major, instance in LibStub:IterateLibraries() do |
manuallyFinalize(major, instance) |
end |
end) |
for major, instance in LibStub:IterateLibraries() do |
manuallyFinalize(major, instance) |
end |
-- Expose the library in the global environment |
_G[ACELIBRARY_MAJOR] = self |
if oldDeactivate then |
oldDeactivate(oldLib) |
end |
end |
if not previous then |
previous = AceLibrary |
end |
if not previous.libs then |
previous.libs = {} |
end |
AceLibrary.libs = previous.libs |
if not previous.positions then |
previous.positions = setmetatable({}, { __mode = "k" }) |
end |
AceLibrary.positions = previous.positions |
AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate, nil) |
Waterfall-1.0/Changelog-Waterfall-1.0-r76021.txt |
Waterfall-1.0/embeds.xml |
Waterfall-1.0/Waterfall-1.0.toc |
Waterfall-1.0/Waterfall-1.0/Waterfall-1.0.lua |
<?xml version="1.0" encoding="utf-8"?><item xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><interface xmlns="http://www.wowace.com/xmlns/wowaddon/">20400</interface><author>Nargiddley</author><category>Library</category><description>GUI Configuration Library</description><enclosure length="24370" type="application/zip" url="http://files.wowace.com/Waterfall-1.0/no-ext/Waterfall-1.0-r76021.zip" /><guid>http://files.wowace.com/Waterfall-1.0/no-ext/Waterfall-1.0-r76021.zip</guid><pubDate>Wed, 04 Jun 2008 20:14:26 +0000</pubDate><stable xmlns="http://www.wowace.com/xmlns/wowaddon/">false</stable><title>Waterfall-1.0</title><version xmlns="http://www.wowace.com/xmlns/wowaddon/">76021</version></item> |
--[[ |
Name: Waterfall-1.0 |
Revision: $Revision: 76021 $ |
Author(s): Nargiddley (nargiddley@gmail.com) |
Inspired By: Dewdrop by ckknight |
Website: http://www.wowace.com/wiki/Waterfall-1.0 |
Documentation: http://www.wowace.com/wiki/Waterfall-1.0 |
SVN: http://svn.wowace.com/wowace/trunk/Waterfall-1.0 |
Description: Gui Configuration Library |
License: LGPL 2.1 |
Dependencies: AceOO-2.0 |
]] |
local MAJOR_VERSION = "Waterfall-1.0" |
local MINOR_VERSION = "$Revision: 76021 $" |
local CONTROL_LIMIT = 250 |
local _ |
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end |
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end |
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end |
local AceOO = AceLibrary("AceOO-2.0") |
local Waterfall = {} |
local currentframe |
local OPTIONS = "Options" |
local ARROW = " -> " |
local ARE_YOU_SURE = "Are you sure you want to %s?" |
if GetLocale() == "zhCN" then |
OPTIONS = "é项" |
ARROW = " -> " |
ARE_YOU_SURE = "ä½ ç¡®å®è¦ %sä¹?" |
elseif GetLocale() == "zhTW" then |
OPTIONS = "é¸é " |
ARROW = " > " |
ARE_YOU_SURE = "ä½ ç¢ºå®è¦ã%sãå?" |
elseif GetLocale() == "koKR" then |
OPTIONS = "ì¤ì " |
ARE_YOU_SURE = "ì ë§ ë¹ì ì `%s'|1ì;를; íìê² ìµëê¹?" |
end |
local DEFAULT_CONTROL_WIDTH = 180 |
--[[ |
Passing an AceOO class will get an object of that class from the pool |
You dont need to specify the class when releasing it |
Passing a string will get a table from a named pool |
the string must be passed when releasing tables like this |
--]] |
local getObj, releaseObj |
do |
local objPools = {} |
function getObj(class,...) |
if not objPools[class] then |
objPools[class] = {} |
end |
local newObj = tremove(objPools[class]) |
if not newObj then |
if type(class.new) == "function" then |
newObj = class:new(...) |
else |
newObj = {} |
end |
end |
return newObj |
end |
function releaseObj(obj,class) |
if not class then |
class = obj.class |
end |
if not class then error("You must supply a class to release non AceOO objects") end |
if not objPools[class] then |
objPools[class] = {} |
end |
if type(obj.CleanUp) == "function" then |
obj:CleanUp() |
end |
tinsert(objPools[class],obj) |
end |
end |
--borrowed from Dewdrop-2.0 |
local function new(...) |
local t = {} |
for i = 1, select('#', ...), 2 do |
local k = select(i, ...) |
if k then |
t[k] = select(i+1, ...) |
else |
break |
end |
end |
return t |
end |
local tmp |
do |
local t = {} |
function tmp(...) |
for k in pairs(t) do |
t[k] = nil |
end |
for i = 1, select('#', ...), 2 do |
local k = select(i, ...) |
if k then |
t[k] = select(i+1, ...) |
else |
break |
end |
end |
return t |
end |
end |
local tmp2 |
do |
local t = {} |
function tmp2(...) |
for k in pairs(t) do |
t[k] = nil |
end |
for i = 1, select('#', ...), 2 do |
local k = select(i, ...) |
if k then |
t[k] = select(i+1, ...) |
else |
break |
end |
end |
return t |
end |
end |
local function getArgs(t, str, num, ...) |
local x = t[str .. num] |
if x == nil then |
return ... |
else |
return x, getArgs(t, str, num + 1, ...) |
end |
end |
local function confirmPopup(message, func, ...) |
if not StaticPopupDialogs["WATERFALL01_CONFIRM_DIALOG"] then |
StaticPopupDialogs["WATERFALL01_CONFIRM_DIALOG"] = {} |
end |
local t = StaticPopupDialogs["WATERFALL01_CONFIRM_DIALOG"] |
for k in pairs(t) do |
t[k] = nil |
end |
t.text = message |
t.button1 = ACCEPT |
t.button2 = CANCEL |
t.OnAccept = function() |
func(unpack(t)) |
end |
for i = 1, select('#', ...) do |
t[i] = select(i, ...) or false |
end |
t.timeout = 0 |
t.whileDead = 1 |
t.hideOnEscape = 1 |
StaticPopup_Show("WATERFALL01_CONFIRM_DIALOG") |
end |
local function showGameTooltip(this) |
if this.tooltipTitle or this.tooltipText then |
GameTooltip_SetDefaultAnchor(GameTooltip, this.frame or this) |
if this.tooltipTitle then |
GameTooltip:SetText(this.tooltipTitle, 1, 1, 1, 1) |
if this.tooltipText then |
GameTooltip:AddLine(this.tooltipText, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1) |
end |
else |
GameTooltip:SetText(this.tooltipText, 1, 1, 1, 1) |
end |
GameTooltip:Show() |
end |
end |
--end of borrowing |
local function parseList(list) |
local token, rest = list:match("([^%s]+)%s+(.+)") |
if not rest then |
return (tonumber(list) or list) |
else |
return (tonumber(token) or token), parseList(rest) |
end |
end |
local function compareOptions(a,b) |
if not a then |
return true |
end |
if not b then |
return false |
end |
local A, B = a.order or 100, b.order or 100 |
if A == B then |
local NameA = a.guiName or a.name or "" |
local NameB = b.guiName or b.name or "" |
return NameA:upper() < NameB:upper() |
end |
if A < 0 then |
if B > 0 then |
return false |
end |
else |
if B < 0 then |
return true |
end |
end |
return A < B |
end |
function Waterfall:Register(id,...) |
local settings = new(...) |
if settings.aceOptions then |
assert(type(settings.aceOptions) == "table","\"aceOptions\" must be a table") |
else |
assert(type(settings.tree) == "table","\"tree\" must be a table") |
assert(type(settings.children) == "function","\"children\" must be a function") |
end |
if not settings.title then |
settings.title = id |
end |
if settings.treeType == "SECTIONS" then |
settings.hideTreeRoot = true |
settings.treeLevels = 3 |
end |
if self.registry[id] then |
self:UnRegister(id) |
end |
self.registry[id] = settings |
end |
function Waterfall:UnRegister(id) |
self.registry[id] = nil |
end |
function Waterfall:IsRegistered(id) |
return not not self.registry[id] |
end |
local function releaseTree(tree) |
for i, v in ipairs(tree) do |
tree[i] = nil |
releaseTree(v) |
releaseObj(v,"table") |
end |
for k, v in pairs(tree) do |
tree[k] = nil |
end |
end |
local function checkNegatable(attr,handler,param) |
local ret |
if type(attr) == "function" then |
ret = attr(param) |
elseif type(attr) == "string" and handler then |
local neg = attr:match("^~(.+)$") |
if neg then |
attr = neg |
end |
ret = handler[attr](handler,param) |
if neg then |
ret = not ret |
end |
else |
ret = attr |
end |
return ret |
end |
local function buildAceOptionsTree(tree, status, options, numLevels, path, rootHandler) |
local settings = Waterfall.registry[currentframe.id] |
local handler = options.handler or rootHandler |
local args = options.args |
assert(type(args) == "table",("args must be a table: Current Path:%s"):format(path or ".")) |
local root |
if path then |
tree.id = path |
tree.text = options.guiName or options.name or (options.handler and options.handler.name) or path |
tree.name = tree.text |
tree.tooltipTitle = tree.text |
tree.tooltipText = options.desc |
tree.order = options.order |
tree.disabled = checkNegatable(options.disabled, handler, options.passValue) |
tree.handler = handler |
tree.icon = options.icon |
tree.hidden = checkNegatable(options.wfHidden or options.guiHidden or options.hidden,handler, options.passValue) |
local hasOptions |
for k, item in pairs(args) do |
if item.type ~= "group" and item.type ~= "heading" then |
if not checkNegatable(item.wfHidden or item.guiHidden or item.hidden,handler, item.passValue) then |
hasOptions = true |
end |
end |
end |
tree.hasOptions = hasOptions |
else |
releaseTree(tree) |
local hasOptions |
for k, item in pairs(args) do |
if item.type ~= "group" and item.type ~= "heading" then |
hasOptions = true |
end |
end |
if hasOptions then |
root = getObj("table") |
root.text = options.guiName or options.name or (options.handler and options.handler.name) or path |
root.name = tree.text |
root.tooltipTitle = tree.text |
root.tooltipText = options.desc |
root.order = options.order |
root.disabled = checkNegatable(options.disabled, handler, options.passValue) |
root.handler = handler |
root.icon = options.icon |
root.hidden = checkNegatable(options.wfHidden or options.guiHidden or options.hidden,handler, options.passValue) |
root.id = "." |
root.hasOptions = true |
if status['.'] == nil then |
status['.'] = true |
end |
tinsert(tree,root) |
if not settings.hideTreeRoot then |
tree = root |
end |
end |
settings.treeRootShown = hasOptions and not settings.hideTreeRoot |
end |
if not numLevels or numLevels > 1 then |
for k, item in pairs(args) do |
if item.type == "group" then |
local child = getObj("table") |
local nextPath |
if path then |
nextPath = path.."."..k |
else |
nextPath = k |
end |
if numLevels then |
buildAceOptionsTree(child, status, item,numLevels - 1,nextPath, handler) |
else |
buildAceOptionsTree(child, status, item,nil,nextPath, handler) |
end |
tinsert(tree,child) |
end |
end |
end |
sort(tree,compareOptions) |
end |
local function checkAceOptionsPath(path, options, frame) |
local t = options |
local validpath |
for k in path:gmatch("[^\.]+") do |
if tonumber(k) and t.args[tonumber(k)] then |
k = tonumber(k) |
end |
if t.args and t.args[k] then |
t = t.args[k] |
if validpath then |
validpath = validpath..'.'..k |
else |
validpath = k |
end |
else |
if frame then |
frame:SetSelected(validpath, true) |
end |
break |
end |
end |
return validpath or '.' |
end |
local function feedFromOptionsTree(paneid, options) |
if not paneid then return end |
local settings = Waterfall.registry[currentframe.id] |
paneid = checkAceOptionsPath(paneid, options, settings.frame) |
local curlevel = 1 |
local levels = 1 |
for x in paneid:gmatch("[^%.]+") do |
curlevel = curlevel + 1 |
end |
if settings.treeLevels == curlevel then |
levels = nil |
end |
if paneid == "." then |
Waterfall:FeedAceOptionsTable(options, nil, levels) |
return options.name or (options.handler and options.handler.name) or OPTIONS |
else |
local crumbs |
if settings.treeRootShown then |
crumbs = options.name or (options.handler and options.handler.name) or OPTIONS |
end |
local t = options |
Waterfall:FeedAceOptionsTable(options,paneid,levels) |
for k in paneid:gmatch("[^\.]+") do |
if tonumber(k) and t.args[tonumber(k)] then |
k = tonumber(k) |
end |
if t.args and t.args[k] then |
t = t.args[k] |
if crumbs then |
crumbs = crumbs..ARROW..(t.guiName or t.name or "") |
else |
crumbs = (t.guiName or t.name or "") |
end |
else |
Waterfall:error("Invalid path \"%s\" for Aceoptions table",paneid) |
end |
end |
return crumbs |
end |
end |
local old_CloseSpecialWindows |
function Waterfall:Open(id, pane) |
if not old_CloseSpecialWindows then |
old_CloseSpecialWindows = CloseSpecialWindows |
CloseSpecialWindows = function() |
local found = old_CloseSpecialWindows() |
return self:CloseAll() or found |
end |
end |
local info = self.registry[id] |
if not info then |
error("You cannot open a waterfall without registering it first") |
end |
--already open |
if info.frame then |
if pane then |
info.frame:SetSelected(pane) |
end |
return |
end |
local frame = getObj(WaterfallFrame) |
frame:SetID(id) |
frame.lib = self |
info.frame = frame |
if info.title then |
frame:SetTitle(info.title) |
end |
local r,g,b = info.colorR or 1, info.colorG or 0.6, info.colorB or 0 |
frame:SetColor(r,g,b) |
if info.aceOptions then |
if not info.tree then |
info.tree = {} |
end |
if not info.treestatus then |
info.treestatus = {} |
end |
currentframe = frame |
frame:SetTree(info.tree, info.treestatus, buildAceOptionsTree, info.aceOptions, info.treeLevels) |
currentframe = nil |
frame:SetChildren(feedFromOptionsTree, info.aceOptions) |
if pane then |
frame.treeview:Collapse() |
end |
frame:SetSelected(pane or info.defaultPane or ".") |
frame.treeview:SetType(info.treeType) |
else |
if not info.treestatus then |
info.treestatus = {} |
end |
frame:SetTree(info.tree, info.treestatus) |
frame:SetChildren(info.children) |
if pane or info.defaultPane then |
frame:SetSelected(pane or info.defaultPane) |
end |
frame.treeview:SetType(info.treeType) |
end |
if info.height then |
frame:SetHeight(info.height) |
end |
if info.width then |
frame:SetWidth(info.width) |
end |
frame:Refresh() |
frame:Show() |
end |
function Waterfall:IsOpen(id) |
return not not self.registry[id].frame |
end |
function Waterfall:Refresh(id) |
local info = self.registry[id] |
if not info then |
error("You can't refresh a waterfall without registering it first") |
end |
if info.frame then |
info.frame:Refresh() |
end |
end |
function Waterfall:SetSize(id, width, height) |
local info = self.registry[id] |
if not info then |
error("You refresh open a waterfall without registering it first") |
end |
if info.frame then |
info.frame:SetWidth(width) |
info.frame:SetHeight(height) |
end |
end |
local function setCommonAttributes(control,info) |
control.width = info.width |
control.height = info.height |
control.noNewLine = info.noNewLine |
control.causesRefresh = info.causesRefresh |
control.fullRefresh = info.fullRefresh |
control.treeRefresh = info.treeRefresh |
control.tooltipTitle = info.tooltipTitle |
control.tooltipText = info.tooltipText |
control:SetText(info.text or "") |
end |
function Waterfall:AddControl(...) |
currentframe.controlcount = currentframe.controlcount + 1 |
if not currentframe then |
error("AddControl must be called from within a children function") |
end |
local control |
local info = self.registry[currentframe.id] |
local limit = info.controlLimit or CONTROL_LIMIT |
if currentframe.controlcount > limit then |
if currentframe.controlcount == limit + 1 then |
control = getObj(WaterfallLabel) |
control.r = 1 |
control.g = 0 |
control.b = 0 |
control:SetText(("More than %s Controls in current pane....."):format(limit)) |
control:Refresh() |
currentframe:AddControl(control) |
end |
return |
end |
local info = tmp2(...) |
if info.type == "label" then |
control = getObj(WaterfallLabel) |
setCommonAttributes(control,info) |
control.justifyH = info.justifyH |
control.r = info.r |
control.g = info.g |
control.b = info.b |
control:Refresh() |
elseif info.type == "dragLink" then |
control = getObj(WaterfallDragLink) |
setCommonAttributes(control,info) |
control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) |
control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) |
if type(info.disabled) == "function" then |
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) |
control.inverseDisabled = info.inverseDisabled |
else |
control.disabled = info.disabled |
end |
control:Refresh() |
elseif info.type == "linklabel" then |
control = getObj(WaterfallLinklabel) |
setCommonAttributes(control,info) |
control.justifyH = info.justifyH |
control.r = info.r |
control.g = info.g |
control.b = info.b |
control:SetFunc("link",info.linkFunc,getArgs(info,"linkArg",1)) |
if type(info.disabled) == "function" then |
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) |
control.inverseDisabled = info.inverseDisabled |
else |
control.disabled = info.disabled |
end |
control.confirm = info.confirm |
control:Refresh() |
elseif info.type == "button" then |
control = getObj(WaterfallButton) |
setCommonAttributes(control,info) |
control:SetFunc("exec",info.execFunc,getArgs(info,"execArg",1)) |
if type(info.disabled) == "function" then |
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) |
control.inverseDisabled = info.inverseDisabled |
else |
control.disabled = info.disabled |
end |
control.confirm = info.confirm |
control.link = info.link |
control:Refresh() |
elseif info.type == "heading" then |
control = getObj(WaterfallHeading) |
control.justifyH = info.justifyH |
setCommonAttributes(control,info) |
control:Refresh() |
elseif info.type == "textbox" then |
control = getObj(WaterfallTextBox) |
setCommonAttributes(control,info) |
control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) |
control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) |
control:SetFunc("changed",info.changedFunc,getArgs(info,"changedArg",1)) |
if type(info.disabled) == "function" then |
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) |
control.inverseDisabled = info.inverseDisabled |
else |
control.disabled = info.disabled |
end |
control.toNumber = info.toNumber |
control.parseList = info.parseList |
control.setOnTextChanged = info.setOnTextChanged |
control:SetFunc("validate",info.validateFunc,getArgs(info,"validateArg",1)) |
control.validateModifies = info.validateModifies |
control:Refresh() |
elseif info.type == "dropdown" then |
control = getObj(WaterfallDropdown) |
setCommonAttributes(control,info) |
control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) |
control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) |
if type(info.disabled) == "function" then |
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) |
control.inverseDisabled = info.inverseDisabled |
else |
control.disabled = info.disabled |
end |
control.parseList = info.parseList |
control.list = info.list |
control.columns = info.columns |
control.strict = info.strict |
control:Refresh() |
elseif info.type == "checkbox" then |
control = getObj(WaterfallCheckBox) |
setCommonAttributes(control,info) |
control:SetChecked(info.checked) |
control.isRadio = info.isRadio |
control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) |
control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) |
if type(info.disabled) == "function" then |
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) |
control.inverseDisabled = info.inverseDisabled |
else |
control.disabled = info.disabled |
end |
control:Refresh() |
elseif info.type == "slider" then |
control = getObj(WaterfallSlider) |
setCommonAttributes(control,info) |
control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) |
control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) |
control.min = info.min |
control.max = info.max |
control.step = info.step |
control.bigStep = info.bigStep or info.step |
control.finalSetOnly = info.finalSetOnly |
if type(info.disabled) == "function" then |
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) |
control.inverseDisabled = info.inverseDisabled |
else |
control.disabled = info.disabled |
end |
control:Refresh() |
elseif info.type == "color" then |
control = getObj(WaterfallColorSwatch) |
setCommonAttributes(control,info) |
control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) |
control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) |
if type(info.disabled) == "function" then |
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) |
control.inverseDisabled = info.inverseDisabled |
else |
control.disabled = info.disabled |
end |
control.hasOpacity = info.hasOpacity |
control.r = info.r |
control.g = info.g |
control.b = info.b |
control.opacity = info.opacity |
control.hasOpacity = info.hasOpacity |
control:Refresh() |
elseif info.type == "keybind" then |
control = getObj(WaterfallKeybinding) |
control:SetText("Binding") |
setCommonAttributes(control,info) |
control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) |
control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) |
if type(info.disabled) == "function" then |
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) |
control.inverseDisabled = info.inverseDisabled |
else |
control.disabled = info.disabled |
end |
control.keybindingOnly = info.keybindingOnly |
control.keybindingExcept = info.keybindingExcept |
control:Refresh() |
else |
error("Invalid or missing type") |
end |
currentframe:AddControl(control) |
end |
local function addDisabled(t,disabled,handler,param) |
if disabled then |
if type(disabled) == "function" or type(disabled) == "boolean" then |
tinsert(t,"disabled") |
tinsert(t,disabled) |
if param then |
tinsert(t,"disabledArg1") |
tinsert(t,param) |
end |
elseif type(disabled) == "string" then |
local neg = disabled:match("^~(.+)$") |
if neg then |
disabled = neg |
end |
if handler and handler[disabled] then |
tinsert(t,"disabled") |
tinsert(t,handler[disabled]) |
tinsert(t,"disabledArg1") |
tinsert(t,handler) |
if param then |
tinsert(t,"disabledArg2") |
tinsert(t,param) |
end |
end |
if neg then |
tinsert(t,"inverseDisabled") |
tinsert(t,true) |
end |
end |
end |
end |
local function appendArgs(t,name,start,...) |
local i = start |
local count = 1 |
local arg = select(count,...) |
while arg do |
tinsert(t,name.."Arg"..i) |
tinsert(t,arg) |
i = i + 1 |
count = count + 1 |
arg = select(count,...) |
end |
end |
local function addFunction(t,name,func,handler,...) |
if type(func) == "function" then |
tinsert(t,name.."Func") |
tinsert(t,func) |
appendArgs(t,name,1,...) |
elseif type(func) == "string" and handler and handler[func] then |
tinsert(t,name.."Func") |
tinsert(t,handler[func]) |
tinsert(t,name.."Arg1") |
tinsert(t,handler) |
appendArgs(t,name,2,...) |
end |
end |
local function validateRange(min,max,step,value) |
value = tonumber(value) |
if step > 0 then |
value = math.floor((value - min) / step + 0.5) * step + min |
end |
if value >= min and value <= max then |
return value |
else |
return nil |
end |
end |
--[[ |
Feeds an ace options table into the right pane, must be called within a "children" function |
root is the root of your options table, this is important if you use any methods for resolving the handler |
path is the path into the table seperated by "." e.g. "Items.Compress" to feed the table given by root.args.Items.args.Compress |
maxLevels is how many sub groups to feed as well, 1 is only the current level 2 will feed sub groups etc. nil assumes no limit |
]] |
function Waterfall:FeedAceOptionsTable(root,path,maxLevels) |
local feedtmp = getObj("table") |
local feedkeys = getObj("table") |
local tmpargs = getObj("table") |
local t |
local handler = root.handler |
local groupDisabled = checkNegatable(root.disabled,root.handler,root.passValue) |
local passSet |
local passGet |
if root.pass then |
passSet = root.set |
passGet = root.get |
else |
passSet = nil |
passGet = nil |
end |
if path then |
t = root |
for k in path:gmatch("[^\.]+") do |
if tonumber(k) and t.args[tonumber(k)] then |
k = tonumber(k) |
end |
if t.args and t.args[k] then |
t = t.args[k] |
handler = t.handler or handler |
groupDisabled = checkNegatable(t.disabled,handler,t.passValue) or groupDisabled |
if t.pass then |
passSet = t.set |
passGet = t.get |
else |
passSet = nil |
passGet = nil |
end |
else |
self:error("Invalid path \"%s\" for Aceoptions table",path) |
end |
end |
else |
t = root |
end |
if t.type ~= "group" then |
self:error("You must feed an ace options group") |
end |
for k, v in pairs(t.args) do |
tinsert(feedtmp, v) |
feedkeys[v] = k |
end |
table.sort(feedtmp, compareOptions) |
local lastheader |
for i, v in ipairs(feedtmp) do |
--only add headers if they arent immediately followed by a group |
if lastheader then |
if v.type ~= "group" then |
--self:AddControl("type","label","text","","width",80) |
--self:AddControl("type","heading","text",lastheader,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER","noNewLine",true) |
self:AddControl("type","heading","text",lastheader,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER") |
end |
lastheader = nil |
end |
local hidden |
if v.guiHidden ~= nil then |
hidden = v.guiHidden |
elseif v.wfHidden ~= nil then |
hidden = v.wfHidden |
else |
hidden = v.hidden |
end |
hidden = checkNegatable(hidden,v.handler or handler,v.passValue) |
local name = v.guiName or v.name |
if name and ( checkNegatable(v.disabled,v.handler or handler,v.passValue) or groupDisabled ) then |
name = "|cFF808080"..name |
end |
if not hidden then |
if v.type == "toggle" then |
--self:AddControl("type","label","text","","width",80) |
tinsert(tmpargs,"type") |
tinsert(tmpargs,"checkbox") |
--tinsert(tmpargs,"noNewLine") |
--tinsert(tmpargs,true) |
tinsert(tmpargs,"text") |
tinsert(tmpargs,name) |
tinsert(tmpargs,"tooltipTitle") |
tinsert(tmpargs,name) |
tinsert(tmpargs,"tooltipText") |
tinsert(tmpargs,v.desc or false) |
tinsert(tmpargs,"isRadio") |
tinsert(tmpargs,not not v.isRadio) |
if passGet and v.get == nil then |
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) |
end |
if passSet and v.set == nil then |
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) |
end |
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) |
tinsert(tmpargs,"fullRefresh") |
tinsert(tmpargs,true) |
self:AddControl(unpack(tmpargs)) |
elseif v.type == "dragLink" then |
tinsert(tmpargs,"type") |
tinsert(tmpargs,"dragLink") |
tinsert(tmpargs,"text") |
tinsert(tmpargs,name) |
tinsert(tmpargs,"tooltipText") |
tinsert(tmpargs,v.desc or false) |
tinsert(tmpargs,"tooltipTitle") |
local tooltipTitle = "" .. tostring(v.itemId) |
tinsert(tmpargs,tooltipTitle) |
tinsert(tmpargs,"linkInfo") |
tinsert(tmpargs,v.linkInfo or {}) |
tinsert(tmpargs,"icon") |
tinsert(tmpargs,v.icon or "") |
tinsert(tmpargs,"iconWidth") |
tinsert(tmpargs,v.iconWidth or WaterfallDragLink.defaultIconSize) |
tinsert(tmpargs,"iconHeight") |
tinsert(tmpargs,v.iconHeight or WaterfallDragLink.defaultIconSize) |
if passGet and v.get == nil then |
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) |
end |
if passSet and v.set == nil then |
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"set",v.set,v.handler,v.passValue) |
end |
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) |
self:AddControl(unpack(tmpargs)) |
elseif v.type == "group" then |
if not maxLevels or maxLevels > 1 then |
--self:AddControl("type","label","text","","width",80) |
if checkNegatable(v.disabled,v.handler or handler,v.passValue) or groupDisabled then |
name = "|cFF808080"..name |
end |
--self:AddControl("type","heading","text",name,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER","noNewLine",true) |
self:AddControl("type","heading","text",name,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER") |
local nextPath |
local nextMaxLevels |
if path then |
nextPath = path.."."..feedkeys[v] |
else |
nextPath = feedkeys[v] |
end |
if maxLevels then |
nextMaxLevels = maxLevels - 1 |
end |
self:FeedAceOptionsTable(root,nextPath,nextMaxLevels) |
self:AddControl("type","heading") |
end |
elseif v.type == "text" then |
if type(v.validate) == "table" then |
if v.multiToggle then |
self:AddControl("type","heading","text",name) |
for key, value in pairs(v.validate) do |
if type(key) == "number" then |
key = value |
end |
self:AddControl("type","label","text","","width",12) |
tinsert(tmpargs,"type") |
tinsert(tmpargs,"checkbox") |
tinsert(tmpargs,"text") |
tinsert(tmpargs,value) |
tinsert(tmpargs,"noNewLine") |
tinsert(tmpargs,true) |
tinsert(tmpargs,"tooltipTitle") |
tinsert(tmpargs,name) |
tinsert(tmpargs,"tooltipText") |
tinsert(tmpargs,value) |
if v.passValue then |
if passGet and v.get == nil then |
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v],key) |
else |
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue,key) |
end |
if passSet and v.set == nil then |
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v],key) |
else |
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue,key) |
end |
else |
if passGet and v.get == nil then |
addFunction(tmpargs,"get",passGet,v.handler or handler,feedkeys[v],key) |
else |
addFunction(tmpargs,"get",v.get,v.handler or handler,key) |
end |
if passSet and v.set == nil then |
addFunction(tmpargs,"set",passSet,v.handler or handler,feedkeys[v],key) |
else |
addFunction(tmpargs,"set",v.set,v.handler or handler,key) |
end |
end |
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue,key) |
tinsert(tmpargs,"fullRefresh") |
tinsert(tmpargs,true) |
self:AddControl(unpack(tmpargs)) |
for i = 0,#tmpargs do |
tmpargs[i] = nil |
end |
end |
else |
--self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT") |
self:AddControl("type","label","text",name) |
tinsert(tmpargs,"type") |
tinsert(tmpargs,"dropdown") |
tinsert(tmpargs,"strict") |
tinsert(tmpargs,true) |
--tinsert(tmpargs,"noNewLine") |
--tinsert(tmpargs,true) |
tinsert(tmpargs,"tooltipTitle") |
tinsert(tmpargs,name) |
tinsert(tmpargs,"tooltipText") |
tinsert(tmpargs,v.desc or false) |
if passGet and v.get == nil then |
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) |
end |
if passSet and v.set == nil then |
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) |
end |
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) |
tinsert(tmpargs,"list") |
tinsert(tmpargs,v.validate) |
tinsert(tmpargs,"columns") |
tinsert(tmpargs,v.columns) |
tinsert(tmpargs,"parseList") |
tinsert(tmpargs, not not v.input) |
tinsert(tmpargs,"fullRefresh") |
tinsert(tmpargs,true) |
self:AddControl(unpack(tmpargs)) |
end |
elseif v.validate == "keybinding" then |
--self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT") |
self:AddControl("type","label","text",name) |
tinsert(tmpargs,"type") |
tinsert(tmpargs,"keybind") |
--tinsert(tmpargs,"noNewLine") |
--tinsert(tmpargs,true) |
if passGet and v.get == nil then |
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) |
end |
if passSet and v.set == nil then |
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) |
end |
tinsert(tmpargs,"keybindingOnly") |
tinsert(tmpargs,v.keybindingOnly or false) |
tinsert(tmpargs,"keybindingExcept") |
tinsert(tmpargs,v.keybindingExcept or false) |
tinsert(tmpargs,"tooltipTitle") |
tinsert(tmpargs,name) |
tinsert(tmpargs,"tooltipText") |
tinsert(tmpargs,v.desc or false) |
tinsert(tmpargs,"fullRefresh") |
tinsert(tmpargs,true) |
self:AddControl(unpack(tmpargs)) |
else |
--self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT") |
self:AddControl("type","label","text",name) |
tinsert(tmpargs,"type") |
tinsert(tmpargs,"textbox") |
--tinsert(tmpargs,"noNewLine") |
--tinsert(tmpargs,true) |
tinsert(tmpargs,"tooltipTitle") |
tinsert(tmpargs,name) |
tinsert(tmpargs,"tooltipText") |
tinsert(tmpargs,v.desc or false) |
if passGet and v.get == nil then |
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) |
end |
if passSet and v.set == nil then |
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) |
end |
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) |
tinsert(tmpargs,"parseList") |
tinsert(tmpargs, not not v.input) |
tinsert(tmpargs,"fullRefresh") |
tinsert(tmpargs,true) |
addFunction(tmpargs,"validate",v.validate,v.handler or handler) |
self:AddControl(unpack(tmpargs)) |
end |
elseif v.type == "execute" then |
--self:AddControl("type","label","text","","width",80) |
tinsert(tmpargs,"type") |
tinsert(tmpargs,"button") |
--tinsert(tmpargs,"noNewLine") |
--tinsert(tmpargs,true) |
tinsert(tmpargs,"text") |
tinsert(tmpargs,name) |
tinsert(tmpargs,"confirm") |
tinsert(tmpargs, v.confirm or false) |
tinsert(tmpargs,"tooltipTitle") |
tinsert(tmpargs,name) |
tinsert(tmpargs,"tooltipText") |
tinsert(tmpargs,v.desc or false) |
tinsert(tmpargs, "link") |
tinsert(tmpargs,v.link or false) |
addFunction(tmpargs,"exec",v.func,v.handler or handler,v.passValue) |
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) |
tinsert(tmpargs,"fullRefresh") |
tinsert(tmpargs,true) |
self:AddControl(unpack(tmpargs)) |
elseif v.type == "header" then |
if name and ( checkNegatable(v.disabled,v.handler or handler,v.passValue) or groupDisabled ) then |
lastheader = "|cFF808080"..name |
else |
lastheader = name |
end |
elseif v.type == "range" then |
--self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT") |
self:AddControl("type","label","text",name) |
tinsert(tmpargs,"type") |
tinsert(tmpargs,"slider") |
--tinsert(tmpargs,"noNewLine") |
--tinsert(tmpargs,true) |
local min,max,step |
min = v.min or 0 |
max = v.max or 1 |
step = v.step or 0 |
tinsert(tmpargs,"min") |
tinsert(tmpargs,v.min or 0) |
tinsert(tmpargs,"max") |
tinsert(tmpargs,v.max or 1) |
tinsert(tmpargs,"step") |
tinsert(tmpargs,v.step or 0) |
tinsert(tmpargs,"bigStep") |
tinsert(tmpargs,v.bigStep or v.step or 0) |
tinsert(tmpargs,"finalSetOnly") |
tinsert(tmpargs,v.finalSetOnly or false) |
--if no step is given or there are more than 20 steps possible |
--local complex = step == 0 or ((max - min) / step) > 20 |
local complex = true |
tinsert(tmpargs,"text") |
tinsert(tmpargs,"") |
tinsert(tmpargs,"tooltipTitle") |
tinsert(tmpargs,name) |
tinsert(tmpargs,"tooltipText") |
tinsert(tmpargs,v.desc or false) |
if passGet and v.get == nil then |
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) |
end |
if passSet and v.set == nil then |
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) |
end |
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) |
tinsert(tmpargs,"fullRefresh") |
tinsert(tmpargs,true) |
self:AddControl(unpack(tmpargs)) |
if complex then |
tinsert(tmpargs,"type") |
tinsert(tmpargs,"textbox") |
tinsert(tmpargs,"noNewLine") |
tinsert(tmpargs,true) |
tinsert(tmpargs,"toNumber") |
tinsert(tmpargs,true) |
tinsert(tmpargs,"validateModifies") |
tinsert(tmpargs,true) |
tinsert(tmpargs,"width") |
tinsert(tmpargs,60) |
tinsert(tmpargs,"tooltipTitle") |
tinsert(tmpargs,name) |
tinsert(tmpargs,"tooltipText") |
tinsert(tmpargs,v.desc or false) |
if passGet and v.get == nil then |
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) |
end |
if passSet and v.set == nil then |
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) |
end |
addFunction(tmpargs,"validate",validateRange,v.handler or handler,min,max,step) |
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) |
tinsert(tmpargs,"fullRefresh") |
tinsert(tmpargs,true) |
self:AddControl(unpack(tmpargs)) |
end |
elseif v.type == "color" then |
--self:AddControl("type","label","text","","width",80) |
tinsert(tmpargs,"type") |
tinsert(tmpargs,"color") |
--tinsert(tmpargs,"noNewLine") |
--tinsert(tmpargs,true) |
tinsert(tmpargs,"text") |
tinsert(tmpargs,name) |
if passGet and v.get == nil then |
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) |
end |
if passSet and v.set == nil then |
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) |
else |
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) |
end |
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) |
tinsert(tmpargs,"tooltipTitle") |
tinsert(tmpargs,name) |
tinsert(tmpargs,"tooltipText") |
tinsert(tmpargs,v.desc or false) |
tinsert(tmpargs,"hasOpacity") |
tinsert(tmpargs, not not v.hasAlpha) |
self:AddControl(unpack(tmpargs)) |
end |
for i = 0,#tmpargs do |
tmpargs[i] = nil |
end |
end |
end |
for i = 0,#feedtmp do |
feedtmp[i] = nil |
end |
for k in pairs(feedkeys) do |
feedkeys[k] = nil |
end |
releaseObj(feedtmp,"table") |
releaseObj(feedkeys,"table") |
releaseObj(tmpargs,"table") |
end |
function Waterfall:Close(id) |
local info = self.registry[id] |
if not info then |
error("You cannot close a waterfall without registering it first") |
end |
local frame = info.frame |
if frame then |
local OnClose = info.OnClose |
if type(OnClose) == "function" then |
OnClose(id) |
end |
releaseObj(frame) |
end |
info.frame = nil |
end |
function Waterfall:CloseAll() |
local closed |
for id, v in pairs(self.registry) do |
if v.frame then |
self:Close(id) |
closed = true |
end |
end |
return closed |
end |
----------------- |
-- Gui Classes -- |
----------------- |
local FrameBackdrop = { |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 30, bottom = 3 } |
} |
local PaneBackdrop = { |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 23, 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 } |
} |
-- virtual class to contain frame functions |
local FramePassthrough = AceOO.Class() |
FramePassthrough.virtual = true |
function FramePassthrough.prototype:SetPoint(point,relativeTo,relativePoint,xOffset,yOffset) |
self.frame:SetPoint(point,(relativeTo and relativeTo.frame) or relativeTo,relativePoint,xOffset,yOffset) |
end |
function FramePassthrough.prototype:SetAllPoints(frame) |
self.frame:SetAllPoints(frame.frame or frame) |
end |
function FramePassthrough.prototype:SetWidth(...) |
self.frame:SetWidth(...) |
end |
function FramePassthrough.prototype:SetHeight(...) |
self.frame:SetHeight(...) |
end |
function FramePassthrough.prototype:SetMinResize(...) |
self.frame:SetMinResize(...) |
end |
function FramePassthrough.prototype:SetMaxResize(...) |
self.frame:SetMaxResize(...) |
end |
function FramePassthrough.prototype:ClearAllPoints() |
self.frame:ClearAllPoints() |
end |
function FramePassthrough.prototype:Show() |
self.frame:Show() |
end |
function FramePassthrough.prototype:Hide() |
self.frame:Hide() |
end |
function FramePassthrough.prototype:GetHeight() |
return self.frame:GetHeight() |
end |
function FramePassthrough.prototype:GetWidth() |
return self.frame:GetWidth() |
end |
-- Base Class for Gui Controls |
local WaterfallControl = AceOO.Class(FramePassthrough) |
WaterfallControl.virtual = true |
function WaterfallControl.prototype:init(frametype,inherits) |
WaterfallControl.super.prototype.init(self) |
local frame = CreateFrame(frametype or "Frame",nil,nil,inherits) |
self.frame = frame |
frame.obj = self |
frame:SetFrameLevel(10) |
frame:SetFrameStrata("DIALOG") |
end |
function WaterfallControl.prototype:SetParent(parent) |
self.parent = parent |
self.frame:SetParent(parent.scrollchild) |
end |
function WaterfallControl.prototype:Refresh() |
end |
--basic cleanup function, usually overridden |
function WaterfallControl.prototype:CleanUp() |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.width = nil |
self.height = nil |
self.noNewLine = nil |
self.causesRefresh = nil |
self.fullRefresh = nil |
self.treeRefresh = nil |
self.disabled = nil |
self:SetFunc("disabled",nil) |
end |
function WaterfallControl.prototype:SetFunc(funcType,func,...) |
self[funcType.."Func"] = func |
local i = 1 |
while self[funcType.."Arg"..i] do |
self[funcType.."Arg"..i] = nil |
i = i + 1 |
end |
i = 1 |
local arg = (select(i,...)) |
while (select(i,...)) ~= nil do |
self[funcType.."Arg"..i] = arg |
i = i + 1 |
arg = (select(i,...)) |
end |
end |
local function Control_OnEnter(this) |
showGameTooltip(this.obj) |
end |
local function Control_OnLeave(this) |
GameTooltip:Hide() |
end |
--the main waterfall frame |
WaterfallFrame = AceOO.Class(FramePassthrough) |
--the right side pane with gui components in it |
WaterfallPane = AceOO.Class(FramePassthrough) |
--gui components to be placed on the pane |
WaterfallLabel = AceOO.Class(WaterfallControl) |
WaterfallLinklabel = AceOO.Class(WaterfallControl) |
WaterfallCheckBox = AceOO.Class(WaterfallControl) |
WaterfallDragLink = AceOO.Class(WaterfallControl) |
WaterfallButton = AceOO.Class(WaterfallControl) |
WaterfallHeading = AceOO.Class(WaterfallControl) |
WaterfallTextBox = AceOO.Class(WaterfallControl) |
WaterfallSlider = AceOO.Class(WaterfallControl) |
WaterfallColorSwatch = AceOO.Class(WaterfallControl) |
WaterfallDropdown = AceOO.Class(WaterfallControl) |
WaterfallKeybinding = AceOO.Class(WaterfallControl) |
--the left side treeview pane |
WaterfallTreeView = AceOO.Class(FramePassthrough) |
WaterfallTreeLine = AceOO.Class(FramePassthrough) |
WaterfallTreeSection = AceOO.Class(FramePassthrough) |
--------------------------- |
-- Waterfall Frame Class -- |
--------------------------- |
local function frameOnClose(this) |
this.obj.lib:Close(this.obj.id) |
end |
local function closeOnClick(this) |
this.obj:Hide() |
end |
local function frameOnMouseDown(this) |
this:GetParent():StartMoving() |
end |
local function frameOnMouseUp(this) |
this:GetParent():StopMovingOrSizing() |
end |
local function sizerseOnMouseDown(this) |
this:GetParent():StartSizing("BOTTOMRIGHT") |
end |
local function sizersOnMouseDown(this) |
this:GetParent():StartSizing("BOTTOM") |
end |
local function sizereOnMouseDown(this) |
this:GetParent():StartSizing("RIGHT") |
end |
local function sizerOnMouseUp(this) |
this:GetParent():StopMovingOrSizing() |
end |
function WaterfallFrame.prototype:init() |
WaterfallFrame.super.prototype.init(self) |
local frame = CreateFrame("Frame",nil,UIParent) |
self.frame = frame |
frame.obj = self |
frame:SetWidth(550) |
frame:SetHeight(500) |
frame:SetPoint("CENTER",UIParent,"CENTER",0,0) |
frame:EnableMouse() |
frame:SetMovable(true) |
frame:SetResizable(true) |
frame:SetMinResize(500,300) |
frame:SetFrameLevel(1) |
frame:SetFrameStrata("DIALOG") |
frame:SetBackdrop(FrameBackdrop) |
frame:SetBackdropColor(0,0,0) |
frame:SetScript("OnHide",frameOnClose) |
local titlebar = frame:CreateTexture(nil,"BACKGROUND") |
self.titlebar = titlebar |
local titlebar2 = frame:CreateTexture(nil,"BACKGROUND") |
self.titlebar2 = titlebar2 |
local r,g,b = 1, 0.6, 0 |
self.colorR = r |
self.colorG = g |
self.colorB = b |
titlebar:SetPoint("TOPLEFT",frame,"TOPLEFT",3,-4) |
titlebar:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-3,-4) |
titlebar:SetHeight(13) |
titlebar2:SetPoint("TOPLEFT",titlebar,"BOTTOMLEFT",0,0) |
titlebar2:SetPoint("TOPRIGHT",titlebar,"BOTTOMRIGHT",0,0) |
titlebar2:SetHeight(13) |
titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) |
titlebar:SetTexture(r,g,b,1) |
titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) |
titlebar2:SetTexture(r,g,b,1) |
local closebutton = CreateFrame("Button",nil,frame,"UIPanelCloseButton") |
closebutton:SetScript("OnClick", closeOnClick) |
closebutton:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-2,-2) |
self.closebutton = closebutton |
closebutton.obj = self |
local title = CreateFrame("Frame",nil,frame) |
self.title = title |
title:EnableMouse() |
title:SetScript("OnMouseDown",frameOnMouseDown) |
title:SetScript("OnMouseUp", frameOnMouseUp) |
title:SetPoint("TOPLEFT",titlebar,"TOPLEFT",0,0) |
title:SetPoint("BOTTOMRIGHT",titlebar2,"BOTTOMRIGHT",0,0) |
local titletext = title:CreateFontString(nil,"OVERLAY","QuestTitleFont") |
titletext:SetPoint("TOPLEFT",title,"TOPLEFT",0,0) |
titletext:SetPoint("TOPRIGHT",title,"TOPRIGHT",0,0) |
titletext:SetHeight(26) |
titletext:SetShadowColor(0,0,0) |
titletext:SetShadowOffset(1,-1) |
titletext:SetTextColor(1,1,1) |
self.titletext = titletext |
local sizer_se = CreateFrame("Frame",nil,frame) |
sizer_se:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) |
sizer_se:SetWidth(25) |
sizer_se:SetHeight(25) |
sizer_se:EnableMouse() |
sizer_se:SetScript("OnMouseDown",sizerseOnMouseDown) |
sizer_se:SetScript("OnMouseUp", sizerOnMouseUp) |
self.sizer_se = sizer_se |
local 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 |
local treeview = WaterfallTreeView:new(self) |
self.treeview = treeview |
treeview:SetPoint("TOPLEFT",frame,"TOPLEFT",11,-32) |
treeview:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",11,10) |
treeview:SetWidth(200) |
treeview:SetMinResize(200,0) |
treeview:SetMaxResize(400,10000) |
local mainpane = WaterfallPane:new(self) |
mainpane:SetPoint("TOPLEFT",treeview.frame,"TOPRIGHT",-3,0) |
mainpane:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-10,10) |
self.mainpane = mainpane |
end |
function WaterfallFrame.prototype:SetColor(r,g,b) |
self.colorR, self.colorG, self.colorB = r,g,b |
local titlebar = self.titlebar |
local titlebar2 = self.titlebar2 |
titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) |
titlebar:SetTexture(r,g,b,1) |
titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) |
titlebar2:SetTexture(r,g,b,1) |
self.mainpane:SetColor(r,g,b) |
end |
function WaterfallFrame.prototype:GetColor() |
return self.colorR, self.colorG, self.colorB |
end |
function WaterfallFrame.prototype:Hide() |
self.frame:Hide() |
end |
function WaterfallFrame.prototype:Show() |
self.frame:Show() |
end |
function WaterfallFrame.prototype:SetID(id) |
self.id = id |
end |
function WaterfallFrame.prototype:ChangePane(newpane) |
self.mainpane:Clear(newpane) |
self.controlcount = 0 |
currentframe = self |
if self.children then |
local title = self.children(newpane, self.childrenArg) |
self.mainpane.titletext:SetText(title) |
end |
currentframe = nil |
self.mainpane:DoLayout() |
self.mainpane.paneid = newpane |
end |
function WaterfallFrame.prototype:SetSelected(id, noupdate) |
self.treeview:SetSelected(id, noupdate) |
end |
function WaterfallFrame.prototype:SetTitle(title) |
self.titletext:SetText(title) |
end |
function WaterfallFrame.prototype:SetChildren(children, arg) |
self.children = children |
self.childrenArg = arg |
end |
function WaterfallFrame.prototype:SetTree(tree, treestatus, func, ...) |
self.treeview:SetContent(tree, treestatus, func, ...) |
end |
function WaterfallFrame.prototype:AddControl(control) |
self.mainpane:AddControl(control) |
end |
function WaterfallFrame.prototype:Refresh() |
self.treeview:Refresh() |
self:ChangePane(self.treeview:GetSelected()) |
end |
function WaterfallFrame.prototype:CleanUp() |
self.children = nil |
self.tree = nil |
self.treeview:CleanUp() |
self:ChangePane() |
self:Hide() |
end |
function WaterfallFrame.prototype:ReAnchorTree() |
self.treeview:SetPoint("TOPLEFT",self.frame,"TOPLEFT",11,-32) |
self.treeview:SetPoint("BOTTOMLEFT",self.frame,"BOTTOMLEFT",11,10) |
end |
------------------------- |
-- WaterfallPane Class -- |
------------------------- |
local function paneOnSizeChanged(this) |
this.obj:SizeChanged() |
end |
local function paneOnMouseWheel(this,value) |
this.obj:MoveScroll(value) |
end |
local function paneOnSizeChanged(this) |
this.obj:DoLayout() |
end |
local function paneOnScrollValueChanged(this,value) |
this.obj:SetScroll(value) |
end |
local numpanes = 0 |
function WaterfallPane.prototype:init(parent) |
WaterfallPane.super.prototype.init(self) |
numpanes = numpanes + 1 |
self.parent = parent |
local name = "WaterfallPane"..numpanes |
local frame = CreateFrame("Frame",name,parent.frame) |
frame:SetBackdrop(PaneBackdrop) |
frame:SetBackdropBorderColor(0.6, 0.6, 0.6) |
frame:SetBackdropColor(0.1, 0.1, 0.1) |
frame:SetFrameLevel(2) |
frame:SetFrameStrata("DIALOG") |
self.frame = frame |
frame.obj = self |
frame:SetScript("OnSizeChanged",paneOnSizeChanged) |
--local title = CreateFrame("Frame",nil,frame) |
--title:SetPoint("TOPLEFT",frame,"TOPLEFT",4,-4) |
--title:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-4,-4) |
--title:SetHeight(24) |
--title:SetBackdrop(PaneBackdrop) |
--title:SetBackdropColor(0,0,0,0.2) |
--self.title = title |
local scrollframe = CreateFrame("ScrollFrame",nil,frame) |
local scrollchild = CreateFrame("Frame",nil,scrollframe) |
local scrollbar = CreateFrame("Slider",name.."ScrollBar",scrollframe,"UIPanelScrollBarTemplate") |
self.scrollframe = scrollframe |
self.scrollchild = scrollchild |
self.scrollbar = scrollbar |
scrollframe:SetScrollChild(scrollchild) |
scrollframe:SetPoint("TOPLEFT",frame,"TOPLEFT",9,-29) |
scrollframe:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-28,12) |
scrollframe:EnableMouseWheel(true) |
scrollframe:SetScript("OnMouseWheel", paneOnMouseWheel) |
scrollframe:SetScript("OnSizeChanged", paneOnSizeChanged) |
scrollframe.obj = self |
--scrollchild:SetPoint("TOP",scrollframe,"TOP",-20,0) |
--scrollchild:SetPoint("BOTTOMLEFT",scrollframe,"BOTTOMLEFT",0,0) |
scrollchild:SetWidth(400) |
scrollbar:SetPoint("TOPLEFT",scrollframe,"TOPRIGHT",0,-16) |
scrollbar:SetPoint("BOTTOMLEFT",scrollframe,"BOTTOMRIGHT",0,16) |
scrollbar:SetMinMaxValues(0,1000) |
scrollbar:SetValueStep(1) |
scrollbar:SetValue(0) |
scrollbar:SetWidth(16) |
scrollbar:SetScript("OnValueChanged", paneOnScrollValueChanged) |
scrollbar.obj = self |
self.scrollvalue = 0 |
local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") |
titletext:SetPoint("TOPLEFT",frame,"TOPLEFT",8,-5) |
titletext:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-8,-5) |
titletext:SetHeight(16) |
titletext:SetText("") |
titletext:SetTextColor(1,1,1) |
local r,g,b = 1, 0.6, 0 |
self.colorR = r |
self.colorG = g |
self.colorB = b |
local titlebar = frame:CreateTexture(nil,"BACKGROUND") |
self.titlebar = titlebar |
local titlebar2 = frame:CreateTexture(nil,"BACKGROUND") |
self.titlebar2 = titlebar2 |
titlebar:SetPoint("TOPLEFT",frame,"TOPLEFT",4,-4) |
titlebar:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-4,-4) |
titlebar:SetHeight(10) |
titlebar2:SetPoint("TOPLEFT",titlebar,"BOTTOMLEFT",0,0) |
titlebar2:SetPoint("TOPRIGHT",titlebar,"BOTTOMRIGHT",0,0) |
titlebar2:SetHeight(10) |
titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) |
titlebar:SetTexture(r,g,b,1) |
titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) |
titlebar2:SetTexture(r,g,b,1) |
--titletext:SetTextColor(1,1,1) |
self.titletext = titletext |
self.controls = {} |
end |
function WaterfallPane.prototype:GetID() |
return self.parent.id |
end |
function WaterfallPane.prototype:SetScroll(value) |
local frame, child = self.scrollframe, self.scrollchild |
local viewheight = frame:GetHeight() |
local height = child:GetHeight() |
local width, viewwidth = frame:GetWidth(), child:GetWidth() |
local offset |
local xOffset = max(0, (width-viewwidth)/2 ) |
if viewheight > height then |
offset = 0 |
else |
offset = floor((height - viewheight) / 1000.0 * value) |
end |
child:ClearAllPoints() |
child:SetPoint("TOPLEFT",frame,"TOPLEFT",xOffset,offset) |
child.offset = offset |
self.scrollvalue = value |
end |
--[[ |
horiz scrolling version |
function WaterfallPane.prototype:SetScroll(value) |
local frame, child = self.scrollframe, self.scrollchild |
local viewheight = frame:GetWidth() |
local height = child:GetWidth() |
local offset |
if viewheight > height then |
offset = 0 |
else |
offset = floor((height - viewheight) / 1000.0 * value) |
end |
child:ClearAllPoints() |
child:SetPoint("TOPLEFT",frame,"TOPLEFT",-offset,0) |
child:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",-offset,0) |
child.offset = offset |
self.scrollvalue = value |
end |
]] |
function WaterfallPane.prototype:MoveScroll(value) |
local frame, child = self.scrollframe, self.scrollchild |
local height, viewheight = frame:GetHeight(), child:GetHeight() |
if height > viewheight then |
self.scrollbar:Hide() |
else |
self.scrollbar:Show() |
local diff = height - viewheight |
local delta = 1 |
if value < 0 then |
delta = -1 |
end |
self.scrollbar:SetValue(min(max(self.scrollvalue + delta*(1000/(diff/45)),0), 1000)) |
end |
end |
function WaterfallPane.prototype:SetColor(r,g,b) |
self.colorR, self.colorG, self.colorB = r,g,b |
local titlebar = self.titlebar |
local titlebar2 = self.titlebar2 |
titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) |
titlebar:SetTexture(r,g,b,1) |
titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) |
titlebar2:SetTexture(r,g,b,1) |
end |
function WaterfallPane.prototype:Clear(newpane) |
for i = 1, #self.controls do |
releaseObj(self.controls[i]) |
self.controls[i] = nil |
end |
if self.paneid ~= newpane then |
self:SetScroll(0) |
end |
end |
function WaterfallPane.prototype:ClearFocus() |
for i, v in ipairs(self.controls) do |
if v.class == WaterfallTextBox then |
v.frame:ClearFocus() |
end |
end |
end |
function WaterfallPane.prototype:AddControl(control) |
tinsert(self.controls, control) |
control:SetParent(self) |
end |
--simple one under the other layout for now |
function WaterfallPane.prototype:DoLayout() |
local usedheight = 0 |
local maxwidth = 0 |
local rowwidth = 1 |
local rowheight = 1 |
for i, control in ipairs(self.controls) do |
control:ClearAllPoints() |
if i == 1 then |
control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",10,0) |
rowheight = control:GetHeight() |
rowwidth = control:GetWidth() + 10 |
else |
if control.noNewLine then |
control:SetPoint("TOPLEFT",self.controls[i-1],"TOPRIGHT",0,0) |
rowheight = max(rowheight,control:GetHeight()) |
rowwidth = rowwidth + control:GetWidth() |
else |
usedheight = usedheight + rowheight |
maxwidth = max(rowwidth,maxwidth) |
control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",10,-usedheight) |
rowheight = control:GetHeight() |
rowwidth = control:GetWidth() |
end |
end |
control:Show() |
end |
maxwidth = max(rowwidth,maxwidth) |
self.scrollchild:SetHeight(usedheight+rowheight) |
self.scrollchild:SetWidth(maxwidth+5) |
self:FixScroll() |
end |
--[[ |
function WaterfallPane.prototype:DoLayout() |
local colheight = 0 |
local sectionheight = 0 |
local sectionwidth = 0 |
local colwidth = 0 |
local usedwidth = 0 |
local frameheight = self.scrollchild:GetHeight() |
for i, control in ipairs(self.controls) do |
colheight = colheight + control:GetHeight() |
colwidth = max(colwidth, control:GetWidth()) |
control:ClearAllPoints() |
if colheight <= (frameheight - 10) then |
if i == 1 then |
control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",4,0) |
else |
control:SetPoint("TOPLEFT",self.controls[i-1],"BOTTOMLEFT",0,0) |
end |
else |
usedwidth = usedwidth + colwidth + 6 |
colwidth = 0 |
colheight = 0 |
control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",4+usedwidth,0) |
end |
control:Show() |
end |
usedwidth = usedwidth + colwidth |
self.scrollchild:SetWidth(usedwidth) |
self:FixScroll() |
end |
]] |
function WaterfallPane.prototype:SizeChanged() |
self:FixScroll() |
end |
function WaterfallPane.prototype:FixScroll() |
local frame, child = self.scrollframe, self.scrollchild |
local height, viewheight = frame:GetHeight(), child:GetHeight() |
local width, viewwidth = frame:GetWidth(), child:GetWidth() |
local offset = self.scrollchild.offset |
if not offset then |
offset = 0 |
end |
local curvalue = self.scrollbar:GetValue() |
local xOffset = max(0, (width-viewwidth)/2 ) |
if viewheight < height then |
self.scrollbar:Hide() |
--self.scrollbar:SetValue(0) |
child:ClearAllPoints() |
child:SetPoint("TOPLEFT",frame,"TOPLEFT",xOffset,0) |
child.offset = 0 |
else |
self.scrollbar:Show() |
local value = (offset / (viewheight - height) * 1000) |
if value > 1000 then value = 1000 end |
self.scrollbar:SetValue(value) |
self:SetScroll(value) |
if value < 1000 then |
child:ClearAllPoints() |
child:SetPoint("TOPLEFT",frame,"TOPLEFT",xOffset,offset) |
child.offset = offset |
end |
end |
end |
--[[ |
horiz scrolling version |
function WaterfallPane.prototype:FixScroll() |
local frame, child = self.scrollframe, self.scrollchild |
local height, viewheight = frame:GetWidth(), child:GetWidth() |
local offset = self.scrollchild.offset |
if not offset then |
offset = 0 |
end |
local curvalue = self.scrollbar:GetValue() |
if viewheight < height then |
self.scrollbar:EnableMouse(false) |
self.scrollbar:SetValue(0) |
else |
self.scrollbar:EnableMouse(true) |
local value = (offset / (viewheight - height) * 1000) |
if value > 1000 then value = 1000 end |
self.scrollbar:SetValue(value) |
self:SetScroll(value) |
if value < 1000 then |
child:ClearAllPoints() |
child:SetPoint("TOPLEFT",frame,"TOPLEFT",-offset,0) |
child:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",-offset,0) |
child.offset = offset |
end |
end |
end |
]] |
function WaterfallPane.prototype:Refresh(fullRefresh) |
if fullRefresh then |
self.parent:Refresh() |
else |
for i, control in ipairs(self.controls) do |
control:Refresh() |
end |
end |
self:FixScroll() |
end |
----------------- |
-- Label Class -- |
----------------- |
function WaterfallLabel.prototype:init() |
WaterfallLabel.super.prototype.init(self) |
local frame = self.frame |
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
self.text = text |
frame.text = text |
text:SetText("Waterfalllabel") |
text:SetJustifyH("LEFT") |
text:SetTextColor(1,1,1) |
frame:SetHeight(24) |
frame:SetWidth(DEFAULT_CONTROL_WIDTH) |
text:SetHeight(16) |
text:SetPoint("LEFT",frame,"LEFT") |
end |
--fontstrings dont return the correct width untill they have been drawn, wait a frame to set the width |
local function labelFixWidth(this) |
if not this.obj.width then |
this:SetWidth(this.text:GetWidth()+3) |
end |
this:SetScript("OnUpdate",nil) |
end |
function WaterfallLabel.prototype:SetText(text) |
self.text:SetText(text) |
self:Refresh() |
end |
function WaterfallLabel.prototype:CleanUp() |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.width = nil |
self.height = nil |
self.noNewLine = nil |
self.causesRefresh = nil |
self.fullRefresh = nil |
self.treeRefresh = nil |
self:SetText("") |
end |
function WaterfallLabel.prototype:Refresh() |
self.frame:SetHeight(self.height or 16) |
if self.width then |
self.text:ClearAllPoints() |
self.text:SetPoint("LEFT",self.frame,"LEFT") |
self.text:SetPoint("RIGHT",self.frame,"RIGHT") |
self:SetWidth(self.width) |
else |
self.text:ClearAllPoints() |
self.text:SetPoint("LEFT") |
self.frame:SetScript("OnUpdate", labelFixWidth) |
end |
self.text:SetJustifyH(self.justifyH or "LEFT") |
self.text:SetTextColor(self.r or 1, self.g or 1, self.b or 1) |
end |
----------------- |
-- Linklabel Class -- |
----------------- |
local function Linklabel_OnEnter(this) |
local self = this.obj |
if not self.disabled then |
self.highlight:Show() |
showGameTooltip(self) |
end |
end |
local function Linklabel_OnLeave(this) |
this.obj.highlight:Hide() |
GameTooltip:Hide() |
end |
local function linkConfirmFunc(self, refresh, full, func, ...) |
func(...) |
if refresh or full then |
self.parent:Refresh(full) |
end |
end |
local function Linklabel_OnClick(this) |
local self = this.obj |
local func = self.linkFunc |
local confirm = this.obj.confirm |
if func then |
if confirm then |
if type(confirm) == "string" then |
confirmPopup(confirm,linkConfirmFunc,self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"linkArg",1)) |
else |
confirmPopup((ARE_YOU_SURE):format(this.obj.text:GetText()),linkConfirmFunc, |
self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"linkArg",1)) |
end |
else |
func(getArgs(this.obj,"linkArg",1)) |
end |
if self.causesRefresh or self.fullRefresh then |
self.parent:Refresh(self.fullRefresh) |
end |
end |
end |
function WaterfallLinklabel.prototype:init() |
WaterfallLinklabel.super.prototype.init(self, "Button") |
local frame = self.frame |
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
self.text = text |
frame.text = text |
frame:SetScript("OnClick", Linklabel_OnClick) |
frame:SetScript("OnEnter", Linklabel_OnEnter) |
frame:SetScript("OnLeave", Linklabel_OnLeave) |
text:SetText("WaterfallLinklabel") |
text:SetJustifyH("LEFT") |
text:SetTextColor(1,1,1) |
frame:SetHeight(24) |
frame:SetWidth(DEFAULT_CONTROL_WIDTH) |
text:SetHeight(16) |
text:SetPoint("LEFT",frame,"LEFT") |
local highlight = frame:CreateTexture(nil, "BACKGROUND") |
self.highlight = highlight |
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") |
highlight:SetBlendMode("ADD") |
highlight:SetAllPoints(frame) |
highlight:Hide() |
frame:EnableMouse() |
end |
function WaterfallLinklabel.prototype:SetText(text) |
self.text:SetText(text) |
self:Refresh() |
end |
function WaterfallLinklabel.prototype:CleanUp() |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.width = nil |
self.height = nil |
self.noNewLine = nil |
self.causesRefresh = nil |
self.fullRefresh = nil |
self.treeRefresh = nil |
self:SetText("") |
end |
function WaterfallLinklabel.prototype:Refresh() |
self.frame:SetHeight(self.height or 16) |
if self.width then |
self.text:ClearAllPoints() |
self.text:SetPoint("LEFT",self.frame,"LEFT") |
self.text:SetPoint("RIGHT",self.frame,"RIGHT") |
self:SetWidth(self.width) |
else |
self.text:ClearAllPoints() |
self.text:SetPoint("LEFT") |
self.frame:SetScript("OnUpdate", labelFixWidth) |
end |
self.text:SetJustifyH(self.justifyH or "LEFT") |
self.text:SetTextColor(self.r or 1, self.g or 1, self.b or 1) |
end |
function WaterfallLinklabel.prototype:SetColor(r,g,b) |
self.r = r |
self.g = g |
self.b = b |
self:Refresh() |
end |
----------------- |
-- ColorSwatch Class -- |
----------------- |
local tmpt = setmetatable({}, {mode='v'}) |
local function ColorSwatch_OnClick(this) |
local self = this.obj |
if not self.disabled then |
local func = self.setFunc |
local hasOpacity = self.hasOpacity |
for k in pairs(tmpt) do |
tmpt[k] = nil |
end |
for i = 1, 1000 do |
local x = self['setArg'..i] |
if x == nil then |
break |
else |
tmpt[i] = x |
end |
end |
ColorPickerFrame.func = function() |
if func then |
local r,g,b = ColorPickerFrame:GetColorRGB() |
local a = hasOpacity and 1 - OpacitySliderFrame:GetValue() or nil |
local n = #tmpt |
tmpt[n+1] = r |
tmpt[n+2] = g |
tmpt[n+3] = b |
tmpt[n+4] = a |
func(unpack(tmpt)) |
self:SetColor(r,g,b,a) |
tmpt[n+1] = nil |
tmpt[n+2] = nil |
tmpt[n+3] = nil |
tmpt[n+4] = nil |
if self.causesRefresh or self.fullRefresh then |
self.parent:Refresh(self.fullRefresh) |
end |
end |
end |
ColorPickerFrame.hasOpacity = self.hasOpacity |
ColorPickerFrame.opacityFunc = ColorPickerFrame.func |
ColorPickerFrame.opacity = 1 - (self.opacity or 0) |
ColorPickerFrame:SetColorRGB(self.r, self.g, self.b) |
local r, g, b, a = self.r, self.g, self.b, self.opacity |
ColorPickerFrame.cancelFunc = function() |
if func then |
local n = #tmpt |
tmpt[n+1] = r |
tmpt[n+2] = g |
tmpt[n+3] = b |
tmpt[n+4] = a |
func(unpack(tmpt)) |
self:SetColor(r,g,b,a) |
for i = 1, n+4 do |
tmpt[i] = nil |
end |
end |
end |
ShowUIPanel(ColorPickerFrame) |
end |
end |
local function ColorSwatch_OnEnter(this) |
local self = this.obj |
if not self.disabled then |
self.highlight:Show() |
showGameTooltip(self) |
end |
end |
local function ColorSwatch_OnLeave(this) |
this.obj.highlight:Hide() |
GameTooltip:Hide() |
end |
function WaterfallColorSwatch.prototype:init() |
WaterfallColorSwatch.super.prototype.init(self,"Button") |
local frame = self.frame |
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
self.text = text |
text:SetJustifyH("LEFT") |
text:SetTextColor(1,1,1) |
frame:SetHeight(24) |
frame:SetWidth(DEFAULT_CONTROL_WIDTH) |
text:SetHeight(24) |
frame:SetScript("OnClick", ColorSwatch_OnClick) |
frame:SetScript("OnEnter",ColorSwatch_OnEnter) |
frame:SetScript("OnLeave",ColorSwatch_OnLeave) |
local colorSwatch = frame:CreateTexture(nil, "OVERLAY") |
self.colorSwatch = colorSwatch |
colorSwatch:SetWidth(24) |
colorSwatch:SetHeight(24) |
colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch") |
local texture = frame:CreateTexture(nil, "OVERLAY") |
colorSwatch.texture = texture |
texture:SetTexture(1, 1, 1) |
texture:SetWidth(13.8) |
texture:SetHeight(13.8) |
texture: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") |
colorSwatch:SetPoint("LEFT", frame, "LEFT", 0, 0) |
text:SetPoint("LEFT",colorSwatch,"RIGHT",2,0) |
text:SetPoint("RIGHT",frame,"RIGHT") |
end |
function WaterfallColorSwatch.prototype:SetText(text) |
self.text:SetText(text) |
end |
function WaterfallColorSwatch.prototype:SetColor(r,g,b,a) |
self.r = r |
self.g = g |
self.b = b |
self.opacity = a |
self:Refresh() |
end |
function WaterfallColorSwatch.prototype:Refresh() |
if self.getFunc then |
self.r, self.g, self.b, self.opacity = self.getFunc(getArgs(self,"getArg",1)) |
end |
if self.disabledFunc then |
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) |
if self.inverseDisabled then |
self.disabled = not self.disabled |
end |
end |
self.colorSwatch.texture:SetTexture(self.r,self.g,self.b) |
if self.disabled then |
self.text:SetTextColor(0.5,0.5,0.5) |
else |
self.text:SetTextColor(1,1,1) |
end |
if self.width then |
self:SetWidth(self.width) |
else |
self:SetWidth(DEFAULT_CONTROL_WIDTH) |
end |
end |
function WaterfallColorSwatch.prototype:CleanUp() |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.width = nil |
self.height = nil |
self.noNewLine = nil |
self.causesRefresh = nil |
self.fullRefresh = nil |
self.treeRefresh = nil |
self:SetText("") |
self:SetFunc("color",nil) |
self.r = nil |
self.g = nil |
self.b = nil |
self.hasOpacity = nil |
self.opacity = nil |
self.disabled = nil |
self:SetFunc("disabled",nil) |
end |
----------------- |
-- Heading Class -- |
----------------- |
function WaterfallHeading.prototype:init() |
WaterfallHeading.super.prototype.init(self) |
local frame = self.frame |
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") |
self.text = text |
frame.text = text |
text:SetJustifyH("LEFT") |
frame:SetHeight(24) |
frame:SetWidth(DEFAULT_CONTROL_WIDTH) |
text:SetHeight(24) |
text:SetPoint("LEFT",frame,"LEFT") |
end |
function WaterfallHeading.prototype:SetText(text) |
self.text:SetText(text) |
self.frame:SetScript("OnUpdate", labelFixWidth) |
end |
function WaterfallHeading.prototype:CleanUp() |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.width = nil |
self.height = nil |
self.noNewLine = nil |
self.causesRefresh = nil |
self.fullRefresh = nil |
self.treeRefresh = nil |
self:SetText("") |
end |
function WaterfallHeading.prototype:Refresh() |
self.frame:SetHeight(self.height or 16) |
if self.width then |
self.text:ClearAllPoints() |
self.text:SetPoint("LEFT",self.frame,"LEFT") |
self.text:SetPoint("RIGHT",self.frame,"RIGHT") |
self:SetWidth(self.width) |
else |
self.text:ClearAllPoints() |
self.text:SetPoint("LEFT") |
self.frame:SetScript("OnUpdate", labelFixWidth) |
end |
self.text:SetJustifyH(self.justifyH or "LEFT") |
end |
----------------- |
-- CheckBox Class -- |
----------------- |
local function CheckBox_OnEnter(this) |
local self = this.obj |
if not self.disabled then |
self.highlight:Show() |
showGameTooltip(self) |
end |
end |
local function CheckBox_OnLeave(this) |
local self = this.obj |
if not self.down then |
self.highlight:Hide() |
end |
GameTooltip:Hide() |
end |
local function CheckBox_OnMouseUp(this) |
local self = this.obj |
if not self.disabled then |
self:ToggleChecked() |
self.text:SetPoint("LEFT",self.check,"RIGHT",0,0) |
end |
self.down = nil |
end |
local function CheckBox_OnMouseDown(this) |
local self = this.obj |
if not self.disabled then |
self.text:SetPoint("LEFT",self.check,"RIGHT",1,-1) |
self.down = true |
end |
end |
function WaterfallCheckBox.prototype:init() |
WaterfallCheckBox.super.prototype.init(self,"Button") |
local frame = self.frame |
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
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(20) |
checkbg:SetHeight(20) |
checkbg:SetPoint("LEFT",frame,"LEFT",0,0) |
checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up") |
local check = frame:CreateTexture(nil,"OVERLAY") |
self.check = check |
check:SetWidth(20) |
check:SetHeight(20) |
check:SetPoint("LEFT",frame,"LEFT",0,0) |
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") |
self:UpdateCheck() |
local highlight = frame:CreateTexture(nil, "BACKGROUND") |
self.highlight = highlight |
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") |
highlight:SetBlendMode("ADD") |
highlight:SetAllPoints(frame) |
highlight:Hide() |
text:SetJustifyH("LEFT") |
text:SetTextColor(1,1,1) |
frame:SetHeight(24) |
frame:SetWidth(DEFAULT_CONTROL_WIDTH) |
text:SetHeight(24) |
text:SetPoint("LEFT",check,"RIGHT",0,0) |
end |
function WaterfallCheckBox.prototype:UpdateTexture() |
local checkbg = self.checkbg |
local check = self.check |
local highlight = self.highlight |
if self.isRadio then |
checkbg:SetTexture("Interface\\Buttons\\UI-RadioButton") |
checkbg:SetTexCoord(0,0.25,0,1) |
check:SetTexture("Interface\\Buttons\\UI-RadioButton") |
check:SetTexCoord(0.5,0.75,0,1) |
check:SetBlendMode("ADD") |
else |
checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up") |
checkbg:SetTexCoord(0,1,0,1) |
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") |
check:SetTexCoord(0,1,0,1) |
check:SetBlendMode("BLEND") |
end |
end |
function WaterfallCheckBox.prototype:SetText(text) |
self.text:SetText(text) |
end |
function WaterfallCheckBox.prototype:UpdateCheck() |
if self.checked then |
self.check:Show() |
else |
self.check:Hide() |
end |
end |
function WaterfallCheckBox.prototype:ToggleChecked() |
self.checked = not self.checked |
if self.setFunc then |
self.setFunc(getArgs(self,"setArg",1,self.checked)) |
end |
if self.causesRefresh or self.fullRefresh then |
self.parent:Refresh(self.fullRefresh) |
else |
self:Refresh() |
end |
end |
function WaterfallCheckBox.prototype:SetChecked(value) |
self.checked = value |
self:UpdateCheck() |
end |
function WaterfallCheckBox.prototype:GetChecked() |
return self.checked |
end |
function WaterfallCheckBox.prototype:CleanUp() |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.highlight:Hide() |
self.width = nil |
self.height = nil |
self.noNewLine = nil |
self.causesRefresh = nil |
self.fullRefresh = nil |
self.treeRefresh = nil |
self:SetText("") |
self:SetChecked(false) |
self:SetFunc("disabled",nil) |
self:SetFunc("get",nil) |
self:SetFunc("set",nil) |
self.isRadio = nil |
end |
function WaterfallCheckBox.prototype:Refresh() |
if self.getFunc then |
local checked = self.getFunc(getArgs(self,"getArg",1)) |
self:SetChecked(checked) |
end |
if self.disabledFunc then |
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) |
if self.inverseDisabled then |
self.disabled = not self.disabled |
end |
end |
if self.disabled then |
self.text:SetTextColor(0.5,0.5,0.5) |
SetDesaturation(self.check, true) |
else |
self.text:SetTextColor(1,1,1) |
SetDesaturation(self.check, false) |
end |
if self.width then |
self:SetWidth(self.width) |
else |
self:SetWidth(DEFAULT_CONTROL_WIDTH) |
end |
self:UpdateTexture() |
end |
----------------- |
-- DragLink Class -- |
----------------- |
-- Provides an icon and description of a link based on on its linkInfo.itemType, itemId, itemInfo |
-- Dragging onto the linkIcon sets linkInfo.itemType, linkInfo.itemId, linkInfo.itemInfo |
-- If linkInfo.itemType == "spell" then linkInfo.spellName, linkInfo.spellRank, linkInfo.spellClass (current player class) are also set |
-- Dragging between two DragLink objects will swap their contents |
function Waterfall:GetDraggingObject() |
return Waterfall.fromObject |
end |
function Waterfall:SetDraggingObject(fromObject) |
Waterfall.fromObject = fromObject |
end |
local function DragLinkOnDragStart(this) |
local fromObject = this.obj |
Waterfall:SetDraggingObject(fromObject) |
end |
local function DragLinkOnReceiveDrag(this) |
local self = this.obj |
local toObject = this.obj |
local fromObject = Waterfall:GetDraggingObject() |
local refreshNeeded = false |
if (fromObject and fromObject ~= toObject) then |
--DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag toObject " .. tostring(toObject)) |
fromObject.itemType, toObject.itemType = toObject.itemType, fromObject.itemType |
fromObject.itemId, toObject.itemId = toObject.itemId, fromObject.itemId |
fromObject.itemInfo, toObject.itemInfo = toObject.itemInfo, fromObject.itemInfo |
fromObject.spellName, toObject.spellName = toObject.spellName, fromObject.spellName |
fromObject.spellRank, toObject.spellRank = toObject.spellRank, fromObject.spellRank |
fromObject.spellClass, toObject.spellClass = toObject.spellClass, fromObject.spellClass |
if fromObject.causesRefresh or fromObject.fullRefresh then |
fromObject.parent:Refresh(fromObject.fullRefresh) |
end |
refreshNeeded = true |
else |
local itemType, itemId, itemInfo = GetCursorInfo() |
--DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag itemType " .. tostring(itemType) .. " itemId " .. tostring(itemId) .. " itemInfo " .. tostring(itemInfo)) |
if (itemType == "item" or itemType == "spell" or itemType == "macro") then |
local linkInfo = self.linkInfo |
linkInfo.itemType = itemType |
linkInfo.itemId = itemId |
linkInfo.itemInfo = itemInfo |
if (itemType == "spell") then |
local spellName, spellRank = GetSpellName(itemId, itemInfo) |
linkInfo.spellName = spellName |
linkInfo.spellRank = spellRank |
linkInfo.spellClass = WaterfallDragLink.CLASS |
else |
linkInfo.spellName = nil |
linkInfo.spellRank = nil |
linkInfo.spellClass = nil |
end |
--DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag itemType " .. tostring(itemType) .. " itemId " .. tostring(itemId) .. " itemInfo " .. tostring(itemInfo)) |
--DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag self.setFunc " .. tostring(self.setFunc) .. " self.disabled " .. tostring(self.disabled)) |
if (self.setFunc and not self.disabled) then |
self.setFunc(getArgs(self,"setArg",1,linkInfo)) |
end |
refreshNeeded = true |
ClearCursor() |
end |
end |
if (refreshNeeded) then |
if self.causesRefresh or self.fullRefresh then |
self.parent:Refresh(self.fullRefresh) |
else |
self:Refresh() |
end |
end |
Waterfall:SetDraggingObject(nil) |
end |
local function DragLinkOnClick(this) |
DragLinkOnReceiveDrag(this) |
end |
local function DragLinkOnEnter(this) |
local self = this.obj |
local linkInfo = self.linkInfo |
if (linkInfo) then |
if (linkInfo.itemType == "item") then |
GameTooltip:SetOwner(this, "ANCHOR_PRESERVE") |
if (linkInfo.itemId) then |
GameTooltip:SetHyperlink("item:" .. linkInfo.itemId .. ":0:0:0:0:0:0:0") |
else |
GameTooltip:SetHyperlink(tostring(linkInfo.itemInfo)) |
end |
elseif (linkInfo.itemType == "spell") then |
if (linkInfo.spellName) then |
-- Cannot set this without preserving spell book & spell id. Even then may be from different class. So lame. Awaiting a Blizzard API that doesnt suck. |
-- GameTooltip:SetSpell(linkInfo.spellId, linkInfo.spellTab) |
GameTooltip:SetOwner(this, "ANCHOR_PRESERVE") |
GameTooltip:AddLine(linkInfo.spellName, 1, 1, 1) |
local name, rank, icon, cost, isFunnel, powerType, castTime, minRange, maxRange = GetSpellInfo(linkInfo.spellName) |
if (name) then |
if (powerType == 0) then |
cost = tostring(cost) .. " " .. MANA |
elseif (powerType == 1) then |
cost = tostring(cost) .. " " .. RAGE |
elseif (powerType == 2) then |
cost = tostring(cost) .. " " .. FOCUS |
elseif (powerType == 3) then |
cost = tostring(cost) .. " " .. ENERGY |
elseif (powerType == 4) then |
cost = tostring(cost) .. " " .. HAPPINESS |
end |
local range = "" |
if (maxRange) then |
range = SPELL_RANGE:format(maxRange) |
end |
GameTooltip:AddDoubleLine(cost, range, 1, 1, 1, 1, 1, 1) |
if (castTime and castTime > 0) then |
GameTooltip:AddLine(SPELL_CAST_TIME_SEC:format(castTime), 1, 1, 1) |
else |
GameTooltip:AddLine(SPELL_CAST_TIME_INSTANT, 1, 1, 1) |
end |
if (rank) then |
GameTooltip:AddLine(tostring(rank)) |
end |
GameTooltip:Show() |
end |
end |
elseif (linkInfo.itemType == "macro") then |
if (linkInfo.itemId) then |
local name, icon, body = GetMacroInfo(linkInfo.itemId) |
if (name) then |
GameTooltip:SetOwner(this, "ANCHOR_PRESERVE") |
GameTooltip:AddLine(tostring(name), 0.2, 0.8, 0.8, 1) |
GameTooltip:AddTexture(icon) |
GameTooltip:AddLine(tostring(body), 1, 1, 1, 1) |
GameTooltip:Show() |
end |
end |
end |
end |
end |
local function DragLinkOnLeave(this) |
GameTooltip:Hide() |
end |
local function DragLinkGetTexture(self) |
local linkInfo = self.linkInfo |
local texture |
if (linkInfo) then |
if (linkInfo.itemType == "item") then |
if (linkInfo.itemId and tonumber(linkInfo.itemId)) then |
_,_,_,_,_,_,_,_,_,texture = GetItemInfo(tonumber(linkInfo.itemId)) |
if (texture) then |
return texture |
end |
end |
elseif (linkInfo.itemType == "spell") then |
if (linkInfo.spellName) then |
_, _, texture = GetSpellInfo(linkInfo.spellName) |
if (texture) then |
return texture |
end |
end |
elseif (linkInfo.itemType == "macro") then |
if (linkInfo.itemId) then |
_, texture = GetMacroInfo(linkInfo.itemId) |
if (texture) then |
return texture |
end |
end |
end |
end |
return "Interface\\Icons\\INV_Misc_Gift_01" |
end |
WaterfallDragLink.defaultIconSize = 36 |
_, WaterfallDragLink.CLASS = UnitClass("player") -- Use the capitalized english class name |
function WaterfallDragLink.prototype:init() |
WaterfallDragLink.super.prototype.init(self, "Button") |
local frame = self.frame |
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
self.text = text |
frame:SetScript("OnDragStart", DragLinkOnDragStart) |
frame:SetScript("OnReceiveDrag", DragLinkOnReceiveDrag) |
frame:SetScript("OnClick", DragLinkOnClick) |
frame:SetScript("OnEnter", DragLinkOnEnter) |
frame:SetScript("OnLeave", DragLinkOnLeave) |
frame:EnableMouse() |
frame:RegisterForDrag("LeftButton") |
frame:RegisterForClicks("LeftButtonUp", "RightButtonUp") |
local linkIcon = frame:CreateTexture(nil, "OVERLAY") |
linkIcon:SetWidth(self.iconWidth or WaterfallDragLink.defaultIconSize) |
linkIcon:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize) |
linkIcon:SetPoint("LEFT",frame,"LEFT",0,0) |
linkIcon:SetTexture(DragLinkGetTexture(self)) |
linkIcon:SetTexCoord(0,1,0,1) |
linkIcon:Show() |
self.linkIcon = linkIcon |
text:SetJustifyH("LEFT") |
text:SetTextColor(1,1,1) |
frame:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize + 4) |
frame:SetWidth(DEFAULT_CONTROL_WIDTH) |
text:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize + 4) |
text:SetPoint("LEFT",check,"RIGHT",0,0) |
end |
function WaterfallDragLink.prototype:SetText(text) |
self.text:SetText(text) |
end |
function WaterfallDragLink.prototype:CleanUp() |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.width = nil |
self.height = nil |
self.noNewLine = nil |
self.causesRefresh = nil |
self.fullRefresh = nil |
self.treeRefresh = nil |
self:SetText("") |
self:SetFunc("disabled", nil) |
self.linkIcon:Hide() |
end |
function WaterfallDragLink.prototype:Refresh() |
if self.disabledFunc then |
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) |
if self.inverseDisabled then |
self.disabled = not self.disabled |
end |
end |
if self.disabled then |
self.frame:Disable() |
else |
self.frame:Enable() |
end |
if self.width then |
self:SetWidth(self.width) |
else |
self:SetWidth(DEFAULT_CONTROL_WIDTH) |
end |
if (not self.linkIcon) then |
local linkIcon = self.frame:CreateTexture(nil, "OVERLAY") |
linkIcon:SetWidth(self.iconWidth or WaterfallDragLink.defaultIconSize) |
linkIcon:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize) |
linkIcon:SetPoint("TOP", self.frame, "TOP", 0, 0) |
linkIcon:SetTexCoord(0,1,0,1) |
linkIcon:Show() |
self.linkIcon = linkIcon |
end |
if self.getFunc then |
self.linkInfo = self.getFunc(getArgs(self,"getArg",1)) |
else |
assert(self.linkInfo, "wth?") |
end |
self.linkIcon:SetTexture(DragLinkGetTexture(self)) |
self.linkIcon:Show() |
if (self.linkInfo.itemId) then |
self:SetText((self.linkInfo.itemInfo or "") .. " (" .. tostring(self.linkInfo.itemId) .. ")") |
else |
self:SetText("") |
end |
--DEFAULT_CHAT_FRAME:AddMessage("WaterfallDragLink.prototype:Refresh texture " .. tostring(DragLinkGetTexture(self)) .. " self.linkInfo.itemId " .. tostring(self.linkInfo.itemId) .. " self.linkInfo.itemInfo " .. tostring(self.linkInfo.itemInfo)) |
end |
----------------- |
-- Button Class -- |
----------------- |
local function buttonConfirmFunc(self, refresh, full, func, ...) |
func(...) |
if refresh or full then |
self.parent:Refresh(full) |
end |
end |
local function Button_OnClick(this) |
local self = this.obj |
local func = self.execFunc |
local confirm = this.obj.confirm |
if func then |
if confirm then |
if type(confirm) == "string" then |
confirmPopup(confirm,buttonConfirmFunc,self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"execArg",1)) |
else |
confirmPopup((ARE_YOU_SURE):format(this.obj.frame:GetText()),buttonConfirmFunc, |
self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"execArg",1)) |
end |
else |
func(getArgs(this.obj,"execArg",1)) |
end |
if self.causesRefresh or self.fullRefresh then |
self.parent:Refresh(self.fullRefresh) |
end |
end |
end |
local function Control_OnEnter(this) |
if this.obj.link then |
GameTooltip:SetOwner(this, "ANCHOR_PRESERVE") |
GameTooltip:SetHyperlink(this.obj.link) |
else |
showGameTooltip(this.obj) |
end |
end |
local function Control_OnLeave(this) |
GameTooltip:Hide() |
end |
function WaterfallButton.prototype:init() |
WaterfallButton.super.prototype.init(self,"Button","UIPanelButtonTemplate") |
local frame = self.frame |
--local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
local text = frame:GetFontString() |
text:SetPoint("LEFT",frame,"LEFT",7,0) |
text:SetPoint("RIGHT",frame,"RIGHT",-7,0) |
frame:SetScript("OnClick",Button_OnClick) |
frame:SetScript("OnEnter",Control_OnEnter) |
frame:SetScript("OnLeave",Control_OnLeave) |
frame:EnableMouse() |
frame:SetHeight(24) |
frame:SetWidth(DEFAULT_CONTROL_WIDTH) |
end |
function WaterfallButton.prototype:SetText(text) |
self.frame:SetText(text) |
end |
function WaterfallButton.prototype:CleanUp() |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.width = nil |
self.height = nil |
self.noNewLine = nil |
self.causesRefresh = nil |
self.fullRefresh = nil |
self.treeRefresh = nil |
self:SetText("") |
self:SetFunc("disabled",nil) |
self:SetFunc("exec",nil) |
end |
function WaterfallButton.prototype:Refresh() |
if self.disabledFunc then |
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) |
if self.inverseDisabled then |
self.disabled = not self.disabled |
end |
end |
if self.disabled then |
self.frame:Disable() |
else |
self.frame:Enable() |
end |
if self.width then |
self:SetWidth(self.width) |
else |
self:SetWidth(DEFAULT_CONTROL_WIDTH) |
end |
end |
----------------- |
-- Keybinding Class -- |
----------------- |
local function Keybinding_OnClick(this, button) |
local self = this.obj |
if button == "LeftButton" or button == "RightButton" then |
if self.waitingForKey then |
this:EnableKeyboard(false) |
self.msgframe:Hide() |
this:UnlockHighlight() |
self.waitingForKey = nil |
else |
this:EnableKeyboard(true) |
self.parent:ClearFocus() |
self.msgframe:Show() |
this:LockHighlight() |
self.waitingForKey = true |
end |
end |
end |
local function Keybinding_OnKeyDown(this, key) |
local self = this.obj |
if self.waitingForKey then |
local keyPressed = key; |
if keyPressed == "ESCAPE" then |
keyPressed = "" |
else |
if ( keyPressed == "BUTTON1" or keyPressed == "BUTTON2" ) then |
return; |
end |
if ( keyPressed == "UNKNOWN" ) then |
return; |
end |
if ( keyPressed == "SHIFT" or keyPressed == "CTRL" or keyPressed == "ALT") then |
return; |
end |
if ( keyPressed == "LSHIFT" or keyPressed == "LCTRL" or keyPressed == "LALT") then |
return; |
end |
if ( keyPressed == "RSHIFT" or keyPressed == "RCTRL" or keyPressed == "RALT") then |
return; |
end |
if ( IsShiftKeyDown() ) then |
keyPressed = "SHIFT-"..keyPressed; |
end |
if ( IsControlKeyDown() ) then |
keyPressed = "CTRL-"..keyPressed; |
end |
if ( IsAltKeyDown() ) then |
keyPressed = "ALT-"..keyPressed; |
end |
end |
if self.setFunc and not self.disabled then |
local validated = true |
if keyPressed == "" then |
keyPressed = nil |
else |
if self.keybindingOnly and not self.keybindingOnly[keyPressed] then |
validated = false |
end |
if self.keybindingExcept and self.keybindingExcept[keyPressed] then |
validated = false |
end |
end |
if validated then |
self.setFunc(getArgs(self,"setArg",1,keyPressed)) |
end |
if self.causesRefresh or self.fullRefresh then |
self.parent:Refresh(self.fullRefresh) |
end |
end |
self:Refresh() |
this:EnableKeyboard(false) |
self.msgframe:Hide() |
this:UnlockHighlight() |
self.waitingForKey = nil |
end |
end |
local function Keybinding_OnMouseDown(this, button) |
if ( button == "LeftButton" or button == "RightButton" ) then |
return |
elseif ( button == "MiddleButton" ) then |
button = "BUTTON3"; |
elseif ( button == "Button4" ) then |
button = "BUTTON4" |
elseif ( button == "Button5" ) then |
button = "BUTTON5" |
end |
Keybinding_OnKeyDown(this, button) |
end |
local function keybindingMsgFixWidth(this) |
this:SetWidth(this.msg:GetWidth()+10) |
this:SetScript("OnUpdate",nil) |
end |
function WaterfallKeybinding.prototype:init() |
WaterfallKeybinding.super.prototype.init(self,"Button","UIPanelButtonTemplate") |
local frame = self.frame |
--local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
local text = frame:GetFontString() |
text:SetPoint("LEFT",frame,"LEFT",7,0) |
text:SetPoint("RIGHT",frame,"RIGHT",-7,0) |
frame:SetScript("OnClick",Keybinding_OnClick) |
frame:SetScript("OnKeyDown",Keybinding_OnKeyDown) |
frame:SetScript("OnEnter",Control_OnEnter) |
frame:SetScript("OnLeave",Control_OnLeave) |
frame:SetScript("OnMouseDown",Keybinding_OnMouseDown) |
frame:RegisterForClicks("AnyDown") |
frame:EnableMouse() |
frame:SetHeight(24) |
frame:SetWidth(DEFAULT_CONTROL_WIDTH) |
local msgframe = CreateFrame("Frame",nil,UIParent) |
msgframe:SetHeight(30) |
msgframe:SetBackdrop(ControlBackdrop) |
msgframe:SetBackdropColor(0,0,0) |
msgframe:SetFrameStrata("DIALOG") |
msgframe:SetFrameLevel(20) |
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("TOP",frame,"BOTTOM",0,-2) |
msgframe:Hide() |
end |
function WaterfallKeybinding.prototype:SetText(text) |
self.frame:SetText(text) |
end |
function WaterfallKeybinding.prototype:CleanUp() |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.width = nil |
self.height = nil |
self.noNewLine = nil |
self.causesRefresh = nil |
self.fullRefresh = nil |
self.treeRefresh = nil |
self:SetText("") |
self:SetFunc("disabled",nil) |
self:SetFunc("exec",nil) |
self.waitingForKey = nil |
self.msgframe:Hide() |
end |
function WaterfallKeybinding.prototype:Refresh() |
if self.getFunc then |
local text = self.getFunc(getArgs(self,"getArg",1)) |
self:SetText(text or "") |
end |
if self.disabledFunc then |
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) |
if self.inverseDisabled then |
self.disabled = not self.disabled |
end |
end |
if self.disabled then |
self.frame:Disable() |
else |
self.frame:Enable() |
end |
if self.width then |
self:SetWidth(self.width) |
else |
self:SetWidth(DEFAULT_CONTROL_WIDTH) |
end |
end |
----------------- |
-- Slider Class -- |
----------------- |
local function Slider_OnValueChanged(this) |
local self = this.obj |
if not this.setup then |
local func = self.setFunc |
local newvalue |
newvalue = this:GetValue() |
if newvalue ~= self.value and not self.disabled then |
if self.bigStep > 0 then |
self.value = math.floor((newvalue - self.min) / self.bigStep + 0.5) * self.bigStep + self.min |
else |
self.value = newvalue |
end |
if func and not self.isInit then |
if (self.finalSetOnly) then |
self.finalSetValue = self.value |
else |
func(getArgs(this.obj,"setArg",1,self.value)) |
end |
end |
end |
if self.value then |
this.obj.valuetext:SetText(math.floor(self.value*10)/10) |
end |
end |
end |
local function Slider_OnMouseUp(this) |
local self = this.obj |
if (self.finalSetValue) then |
local func = self.setFunc |
if func then |
func(getArgs(this.obj,"setArg",1,self.finalSetValue)) |
end |
end |
if self.causesRefresh or self.fullRefresh then |
self.parent:Refresh(self.fullRefresh) |
end |
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 } |
} |
function WaterfallSlider.prototype:init() |
WaterfallSlider.super.prototype.init(self,"Frame") |
local frame = self.frame |
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") |
frame:SetWidth(DEFAULT_CONTROL_WIDTH) |
slider:SetHeight(17) |
slider:SetHitRectInsets(0,0,-10,-10) |
slider:SetBackdrop(SliderBackdrop) |
self.text = slider:CreateFontString(nil,"ARTWORK","GameFontNormalSmall") |
self.text:SetPoint("TOP",frame,"TOP",0,-2) |
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) |
self.valuetext = slider:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall") |
self.valuetext:SetPoint("TOP",slider,"BOTTOM",0,3) |
slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Horizontal") |
frame:SetWidth(DEFAULT_CONTROL_WIDTH) |
frame:SetHeight(40) |
slider:SetPoint("TOP",self.text,"BOTTOM",0,0) |
slider:SetPoint("LEFT",frame,"LEFT",0,0) |
slider:SetPoint("RIGHT",frame,"RIGHT",0,0) |
if self.getFunc then |
self.isInit = true |
self.value = self.getFunc(getArgs(self,"getArg",1)) |
self.isInit = nil |
end |
slider:SetValue(self.value or 0) |
slider:SetScript("OnValueChanged",Slider_OnValueChanged) |
end |
function WaterfallSlider.prototype:CleanUp() |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.width = nil |
self.height = nil |
self.noNewLine = nil |
self.causesRefresh = nil |
self.fullRefresh = nil |
self.treeRefresh = nil |
self:SetText("") |
self:SetFunc("disabled",nil) |
self:SetFunc("get",nil) |
self:SetFunc("set",nil) |
self.min = nil |
self.max = nil |
self.step = nil |
self.value = nil |
self.parent = nil |
self.disabled = nil |
end |
function WaterfallSlider.prototype:SetText(text) |
self.text:SetText(text) |
end |
function WaterfallSlider.prototype:Refresh() |
local frame = self.slider |
frame.setup = true |
frame:SetMinMaxValues(self.min or 0,self.max or 100) |
self.lowtext:SetText(self.min or 0) |
self.hightext:SetText(self.max or 100) |
frame:SetValueStep(self.step or 1) |
frame.setup = nil |
if self.getFunc then |
local newvalue |
newvalue = self.getFunc(getArgs(self,"getArg",1)) |
if newvalue ~= self.value then |
self.isInit = true |
frame:SetValue(newvalue) |
self.isInit = nil |
end |
end |
if self.disabledFunc then |
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) |
if self.inverseDisabled then |
self.disabled = not self.disabled |
end |
end |
if self.disabled then |
self.slider:EnableMouse(false) |
self.text:SetTextColor(.5,.5,.5) |
self.hightext:SetTextColor(.5,.5,.5) |
self.lowtext:SetTextColor(.5,.5,.5) |
self.valuetext:SetTextColor(.5,.5,.5) |
else |
self.slider:EnableMouse(true) |
self.text:SetTextColor(1,1,1) |
self.hightext:SetTextColor(1,1,1) |
self.lowtext:SetTextColor(1,1,1) |
self.valuetext:SetTextColor(1,1,1) |
end |
if self.width then |
self:SetWidth(self.width) |
else |
self:SetWidth(DEFAULT_CONTROL_WIDTH) |
end |
end |
------------------- |
-- TextBox Class -- |
------------------- |
local function TextBox_OnEscapePressed(this) |
this:ClearFocus() |
end |
local function TextBox_OnEnterPressed(this) |
local self = this.obj |
local value = this:GetText() |
if self.toNumber then |
value = tonumber(value) |
--TODO: same message as a failed validation |
if not value then return end |
end |
if self.setFunc and not self.disabled then |
local validated = true |
if type(self.validateFunc) == "function" then |
validated = self.validateFunc(getArgs(self,"validateArg",1,value)) |
if validated and self.validateModifies then |
value = validated |
local setOnTextChanged = self.setOnTextChanged |
self.setOnTextChanged = nil |
this:SetText(value) |
self.setOnTextChanged = setOnTextChanged |
end |
elseif type(self.validateFunc) == "table" then |
validated = self.validateFunc[value] |
end |
--TODO: message for failed validation |
if validated then |
if self.parseList then |
self.setFunc(getArgs(self,"setArg",1,parseList(value))) |
else |
self.setFunc(getArgs(self,"setArg",1,value)) |
end |
end |
if self.getFunc == false then |
self:SetText("") |
end |
if self.causesRefresh or self.fullRefresh then |
self.parent:Refresh(self.fullRefresh) |
end |
end |
end |
local function TextBox_OnTextChanged(this) |
local self = this.obj |
local value = this:GetText() |
if self.setOnTextChanged then |
TextBox_OnEnterPressed(this) |
end |
if self.changedFunc and not self.disabled then |
if self.parseList then |
self.changedFunc(getArgs(self,"changedArg",1,parseList(value))) |
else |
self.changedFunc(getArgs(self,"changedArg",1,value)) |
end |
end |
end |
function WaterfallTextBox.prototype:init() |
WaterfallTextBox.super.prototype.init(self,"EditBox") |
local frame = self.frame |
--local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
--self.text = text |
frame:SetScript("OnEnter",Control_OnEnter) |
frame:SetScript("OnLeave",Control_OnLeave) |
frame:SetFontObject(ChatFontNormal) |
frame:SetScript("OnEscapePressed",TextBox_OnEscapePressed) |
frame:SetScript("OnEnterPressed",TextBox_OnEnterPressed) |
frame:SetScript("OnTextChanged",TextBox_OnTextChanged) |
frame:SetTextInsets(5,5,3,3) |
frame:SetMaxLetters(256) |
frame:SetAutoFocus(false) |
frame:SetHeight(26) |
frame:SetWidth(DEFAULT_CONTROL_WIDTH) |
frame:SetBackdrop(ControlBackdrop) |
frame:SetBackdropColor(0,0,0) |
end |
local function UglyScrollLeft() |
this:HighlightText(0,1); |
this:Insert(" "..strsub(this:GetText(),1,1)); |
this:HighlightText(0,1); |
this:Insert(""); |
this:SetScript("OnUpdate", nil); |
end |
function WaterfallTextBox.prototype:SetText(text) |
self.frame:SetText(text) |
self.frame:SetScript("OnUpdate", UglyScrollLeft); |
end |
function WaterfallTextBox.prototype:CleanUp() |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.width = nil |
self.height = nil |
self.noNewLine = nil |
self.causesRefresh = nil |
self.fullRefresh = nil |
self.treeRefresh = nil |
self.setOnTextChanged = nil |
self:SetText("") |
self:SetFunc("disabled",nil) |
self:SetFunc("get",nil) |
self:SetFunc("set",nil) |
self:SetFunc("changed",nil) |
end |
function WaterfallTextBox.prototype:Refresh() |
if self.getFunc then |
local text = self.getFunc(getArgs(self,"getArg",1)) |
if text then |
self:SetText(text) |
end |
end |
if self.disabledFunc then |
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) |
if self.inverseDisabled then |
self.disabled = not self.disabled |
end |
end |
if self.disabled then |
self.frame:EnableMouse(false) |
self.frame:ClearFocus() |
self.frame:SetTextColor(0.5,0.5,0.5) |
else |
self.frame:EnableMouse(true) |
self.frame:SetTextColor(1,1,1) |
end |
if self.width then |
self:SetWidth(self.width) |
else |
self:SetWidth(DEFAULT_CONTROL_WIDTH) |
end |
end |
-------------------- |
-- DropDown Class -- |
-------------------- |
local function Dropdown_OnEnterPressed(this) |
local self = this.obj |
if self.setFunc and not self.disabled then |
local ret |
if self.strict and this.value then |
ret = this.value |
else |
ret = this:GetText() |
end |
if self.parseList then |
self.setFunc(getArgs(self,"setArg",1,parseList(ret))) |
else |
self.setFunc(getArgs(self,"setArg",1,ret)) |
end |
if self.causesRefresh or self.fullRefresh then |
self.parent:Refresh(self.fullRefresh) |
end |
end |
end |
local function Dropdown_TogglePullout(this) |
local self = this.obj |
if self.open then |
self.open = nil |
self.pullout:Hide() |
else |
self.open = true |
self:BuildPullout() |
if self.lines[1] and self.lines[1]:IsShown() then |
self.pullout:Show() |
end |
end |
end |
local function Dropdown_OnHide(this) |
this.obj.pullout:Hide() |
end |
local function Dropdown_LineClicked(this) |
local self = this.obj |
self.open = false |
self.pullout:Hide() |
self.editbox:SetText(this.text:GetText()) |
self.editbox.value = this.value |
Dropdown_OnEnterPressed(self.editbox) |
end |
local function Dropdown_LineEnter(this) |
this.highlight:Show() |
end |
local function Dropdown_LineLeave(this) |
this.highlight:Hide() |
end |
function WaterfallDropdown.prototype:init() |
WaterfallDropdown.super.prototype.init(self,"Frame") |
local frame = self.frame |
frame.obj = self |
--local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
--self.text = text |
local editbox = CreateFrame("EditBox",nil,frame) |
self.editbox = editbox |
editbox.obj = self |
editbox:SetFontObject(ChatFontNormal) |
editbox:SetScript("OnEscapePressed",TextBox_OnEscapePressed) |
editbox:SetScript("OnEnterPressed",Dropdown_OnEnterPressed) |
frame:SetScript("OnEnter",Control_OnEnter) |
frame:SetScript("OnLeave",Control_OnLeave) |
editbox:SetScript("OnEnter",Control_OnEnter) |
editbox:SetScript("OnLeave",Control_OnLeave) |
editbox:SetTextInsets(5,5,3,3) |
editbox:SetMaxLetters(256) |
editbox:SetAutoFocus(false) |
editbox:SetBackdrop(ControlBackdrop) |
editbox:SetBackdropColor(0,0,0) |
editbox:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
editbox:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-20,0) |
local button = CreateFrame("Button",nil,frame) |
self.button = button |
button.obj = self |
button:SetWidth(24) |
button:SetHeight(24) |
button:SetScript("OnEnter",Control_OnEnter) |
button:SetScript("OnLeave",Control_OnLeave) |
button:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Up") |
button:GetNormalTexture():SetTexCoord(.09,.91,.09,.91) |
button:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Down") |
button:GetPushedTexture():SetTexCoord(.09,.91,.09,.91) |
button:SetDisabledTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Disabled") |
button:GetDisabledTexture():SetTexCoord(.09,.91,.09,.91) |
button:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight", "ADD") |
button:GetHighlightTexture():SetTexCoord(.09,.91,.09,.91) |
button:SetPoint("RIGHT",frame,"RIGHT",0,0) |
button:SetScript("OnClick",Dropdown_TogglePullout) |
frame:SetHeight(26) |
frame:SetWidth(DEFAULT_CONTROL_WIDTH) |
frame:SetScript("OnHide",Dropdown_OnHide) |
local pullout = CreateFrame("Frame",nil,UIParent) |
self.pullout = pullout |
frame:EnableMouse() |
pullout:SetBackdrop(ControlBackdrop) |
pullout:SetBackdropColor(0,0,0) |
pullout:SetFrameStrata("DIALOG") |
pullout:SetFrameLevel(20) |
pullout:SetPoint("TOPLEFT",frame,"BOTTOMLEFT",0,0) |
pullout:SetPoint("TOPRIGHT",frame,"BOTTOMRIGHT",-24,0) |
pullout:SetClampedToScreen(true) |
pullout:Hide() |
self.lines = {} |
end |
function WaterfallDropdown.prototype:SetText(text) |
self.editbox:SetText(text) |
end |
function WaterfallDropdown.prototype:CleanUp() |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.width = nil |
self.height = nil |
self.noNewLine = nil |
self.causesRefresh = nil |
self.fullRefresh = nil |
self.treeRefresh = nil |
self:SetText("") |
self:SetFunc("disabled",nil) |
self:SetFunc("get",nil) |
self:SetFunc("set",nil) |
self.editbox.value = nil |
self.columns = nil |
end |
function WaterfallDropdown.prototype:Refresh() |
local list = self.list |
if self.getFunc then |
local newval = self.getFunc(getArgs(self,"getArg",1)) |
if newval then |
if self.strict then |
if type(list) == "table" then |
local value |
for k, text in pairs(list) do |
if type(k) == "string" then |
value = k |
else |
value = text |
end |
if newval == value then |
self:SetText(text) |
self.editbox.value = value |
break |
end |
end |
elseif type(list) == "function" then |
for i, text, value in list() do |
if newval == value then |
self:SetText(text) |
self.editbox.value = value |
break |
end |
end |
end |
else |
self.editbox:SetText(newval) |
self.editbox.value = newval |
end |
else |
self.editbox:SetText("") |
self.editbox.value = newval |
end |
end |
if self.disabledFunc then |
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) |
if self.inverseDisabled then |
self.disabled = not self.disabled |
end |
end |
if self.disabled then |
self.editbox:EnableMouse(false) |
self.editbox:ClearFocus() |
self.editbox:SetTextColor(0.5,0.5,0.5) |
self.button:Disable() |
else |
self.button:Enable() |
if self.strict then |
self.editbox:EnableMouse(false) |
self.editbox:ClearFocus() |
self.editbox:SetTextColor(1,1,1) |
else |
self.editbox:EnableMouse(true) |
self.editbox:SetTextColor(1,1,1) |
end |
end |
if self.width then |
self:SetWidth(self.width) |
else |
self:SetWidth(DEFAULT_CONTROL_WIDTH) |
end |
end |
local ddsort = {} |
function WaterfallDropdown.prototype:BuildPullout() |
local list = self.list |
local lines = self.lines |
local totalheight = 10 |
self:ClearPullout() |
if type(list) == "table" then |
for k, v in pairs(list) do |
tinsert(ddsort,k) |
end |
table.sort(ddsort) |
local columns = tonumber(self.columns) |
local columnHeight, columnWidth |
local columnCount = 0 |
self.pullout:ClearAllPoints() |
if (columns) then |
columnHeight = math.floor((# ddsort) / columns) + 2 |
columnWidth = self.pullout:GetWidth() |
self.pullout:SetPoint("TOPLEFT",self.frame,"BOTTOMRIGHT", -1 * DEFAULT_CONTROL_WIDTH * columns - 24,0) |
self.pullout:SetPoint("TOPRIGHT",self.frame,"BOTTOMRIGHT",-24,0) |
else |
self.pullout:SetPoint("TOPLEFT",self.frame,"BOTTOMRIGHT", -1 * DEFAULT_CONTROL_WIDTH - 24,0) |
self.pullout:SetPoint("TOPRIGHT",self.frame,"BOTTOMRIGHT",-24,0) |
columnHeight = # ddsort + 1 |
end |
for i, value in pairs(ddsort) do |
local text = list[value] |
if ((i - 1) % (columnHeight - 1) == 0) then |
columnCount = columnCount + 1 |
end |
if (lines[i]) then |
self:UpdateLine(lines[i], math.fmod(i-1, (columnHeight-1)), columnCount) |
else |
lines[i] = self:CreateLine(math.fmod(i-1, (columnHeight-1)), columnCount) |
end |
if i == 1 then |
lines[i]:SetPoint("TOP",self.pullout,"TOP",0,-5) |
else |
if (columns and ((i - 1) % columnHeight == 0)) then |
lines[i]:SetPoint("TOP",self.pullout,"TOP",0,-5) |
else |
lines[i]:SetPoint("TOP",lines[i-1],"BOTTOM",0,0) |
end |
end |
lines[i].text:SetText(text) |
if type(value) == "string" then |
lines[i].value = value |
else |
lines[i].value = text |
end |
if lines[i].value == self.editbox.value and self.getFunc then |
lines[i].check:Show() |
else |
lines[i].check:Hide() |
end |
lines[i]:Show() |
end |
totalheight = totalheight + 17 * (columnHeight-1) |
for k in pairs(ddsort) do |
ddsort[k] = nil |
end |
elseif type(list) == "function" then |
for i, text, value in list() do |
if not lines[i] then |
lines[i] = self:CreateLine() |
if i == 1 then |
lines[i]:SetPoint("TOP",self.pullout,"TOP",0,-5) |
else |
lines[i]:SetPoint("TOP",lines[i-1],"BOTTOM",0,0) |
end |
end |
lines[i].text:SetText(text) |
lines[i].value = value |
lines[i]:Show() |
totalheight = totalheight + 17 |
end |
end |
self.pullout:SetHeight(totalheight) |
end |
function WaterfallDropdown.prototype:ClearPullout() |
if self.lines then |
for i, line in ipairs(self.lines) do |
line.text:SetText("") |
line:Hide() |
end |
end |
self.pullout:SetHeight(10) |
self.pullout:SetWidth(DEFAULT_CONTROL_WIDTH) |
end |
-- For large list, if specified, 0 based row, 1 based column gives a grid. |
function WaterfallDropdown.prototype:CreateLine(row, column) |
local frame = CreateFrame("Button",nil,self.pullout) |
frame.obj = self |
frame.text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
frame.text:SetTextColor(1,1,1) |
frame.text:SetJustifyH("LEFT") |
frame:SetFrameLevel(25) |
frame:SetFrameStrata("DIALOG") |
local highlight = frame:CreateTexture(nil, "OVERLAY") |
frame.highlight = highlight |
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") |
highlight:SetBlendMode("ADD") |
local check = frame:CreateTexture("OVERLAY") |
frame.check = check |
check:SetWidth(16) |
check:SetHeight(16) |
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") |
frame:SetScript("OnClick",Dropdown_LineClicked) |
frame:SetScript("OnEnter",Dropdown_LineEnter) |
frame:SetScript("OnLeave",Dropdown_LineLeave) |
return self:UpdateLine(frame, row, column) |
end |
function WaterfallDropdown.prototype:UpdateLine(frame, row, column) |
if (column) then |
frame:SetPoint("TOPLEFT",self.pullout,"TOPLEFT",6 + (column - 1) * DEFAULT_CONTROL_WIDTH,-5 - row*17) |
frame:SetPoint("BOTTOMRIGHT",self.pullout,"TOPLEFT",-6 + column * DEFAULT_CONTROL_WIDTH,-5 - (row+1)*17) |
else |
frame:SetPoint("LEFT",self.pullout,"LEFT",6,0) |
frame:SetPoint("RIGHT",self.pullout,"RIGHT",-6,0) |
end |
frame.text:SetPoint("TOPLEFT",frame,"TOPLEFT",16,0) |
frame.text:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) |
frame.highlight:SetAllPoints(frame) |
frame.highlight:Hide() |
frame.check:SetPoint("LEFT",frame,"LEFT",0,0) |
return frame |
end |
----------------------------- |
-- WaterfallTreeView Class -- |
----------------------------- |
local function treeviewOnSizeChanged(this) |
this.obj:SizeChanged() |
end |
local function treeviewOnMouseWheel(this,value) |
this.obj:MoveScroll(value) |
end |
local function treeviewOnScrollValueChanged(this,value) |
this.obj:SetScroll(value) |
end |
local function treeviewSizerOnMouseUp(this) |
this:GetParent():StopMovingOrSizing() this.obj.parent:ReAnchorTree() |
end |
function WaterfallTreeView.prototype:init(parent) |
WaterfallTreeView.super.prototype.init(self) |
--Parent WaterfallFrame Object |
self.parent = parent |
local frame = CreateFrame("Frame",nil,parent.frame) |
frame.obj = self |
local scrollframe = CreateFrame("ScrollFrame",nil,frame) |
local scrollchild = CreateFrame("Frame",nil,scrollframe) |
local scrollbar = CreateFrame("Slider",nil,scrollframe,"UIPanelScrollBarTemplate") |
frame:SetBackdrop(ControlBackdrop) |
frame:SetBackdropBorderColor(0.6, 0.6, 0.6) |
frame:SetBackdropColor(0.1, 0.1, 0.1) |
frame:SetScript("OnSizeChanged", treeviewOnSizeChanged) |
frame:SetFrameLevel(2) |
frame:SetFrameStrata("DIALOG") |
frame:SetResizable(true) |
self.scrollframe = scrollframe |
self.scrollchild = scrollchild |
self.frame = frame |
self.scrollbar = scrollbar |
scrollframe:SetScrollChild(scrollchild) |
scrollframe:SetPoint("TOPLEFT",frame,"TOPLEFT",8,-12) |
scrollframe:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-28,12) |
scrollframe:EnableMouseWheel(true) |
scrollframe:SetScript("OnMouseWheel", treeviewOnMouseWheel) |
scrollframe.obj = self |
scrollchild:SetPoint("TOPLEFT",scrollframe,"TOPLEFT",0,0) |
scrollchild:SetPoint("TOPRIGHT",scrollframe,"TOPRIGHT",0,0) |
scrollchild:SetHeight(400) |
scrollbar:SetPoint("TOPLEFT",scrollframe,"TOPRIGHT",0,-16) |
scrollbar:SetPoint("BOTTOMLEFT",scrollframe,"BOTTOMRIGHT",0,16) |
scrollbar:SetMinMaxValues(0,1000) |
scrollbar:SetValueStep(1) |
scrollbar:SetValue(0) |
scrollbar:SetWidth(16) |
scrollbar:SetScript("OnValueChanged", treeviewOnScrollValueChanged) |
self.scrollvalue = 0 |
scrollchild.lines = {} |
self.linetables = {} |
self.sections = {} |
scrollbar.obj = self |
local sizer = CreateFrame("Frame",nil,frame) |
sizer:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0) |
sizer:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) |
sizer:SetWidth(7) |
sizer:EnableMouse() |
sizer:SetScript("OnMouseDown",sizereOnMouseDown) |
sizer:SetScript("OnMouseUp", treeviewSizerOnMouseUp) |
self.sizer = sizer |
sizer.obj = self |
self:Refresh() |
end |
function WaterfallTreeView.prototype:SetScroll(value) |
local frame, child = self.scrollframe, self.scrollchild |
local viewheight = frame:GetHeight() |
local height = child:GetHeight() |
local offset |
if viewheight > height then |
offset = 0 |
else |
offset = floor((height - viewheight) / 1000.0 * value) |
end |
child:ClearAllPoints() |
child:SetPoint("TOPLEFT",frame,"TOPLEFT",0,offset) |
child:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,offset) |
child.offset = offset |
self.scrollvalue = value |
end |
function WaterfallTreeView.prototype:SizeChanged() |
self:FixScroll() |
self.parent:ReAnchorTree() |
end |
function WaterfallTreeView.prototype:MoveScroll(value) |
local frame, child = self.scrollframe, self.scrollchild |
local height, viewheight = frame:GetHeight(), child:GetHeight() |
if height > viewheight then |
self.scrollbar:Hide() |
else |
self.scrollbar:Show() |
local diff = height - viewheight |
local delta = 1 |
if value < 0 then |
delta = -1 |
end |
self.scrollbar:SetValue(math.min(math.max(self.scrollvalue + delta*(1000/(diff/45)),0), 1000)) |
end |
end |
function WaterfallTreeView.prototype:FixScroll() |
if self.noFixScroll then return end |
local frame, child = self.scrollframe, self.scrollchild |
local height, viewheight = frame:GetHeight(), child:GetHeight() |
local offset = self.scrollchild.offset |
if not offset then |
offset = 0 |
end |
local curvalue = self.scrollbar:GetValue() |
if viewheight < height then |
self.scrollbar:Hide() |
self.scrollbar:SetValue(0) |
else |
self.scrollbar:Show() |
local value = (offset / (viewheight - height) * 1000) |
if value > 1000 then value = 1000 end |
self.scrollbar:SetValue(value) |
self:SetScroll(value) |
if value < 1000 then |
child:ClearAllPoints() |
child:SetPoint("TOPLEFT",frame,"TOPLEFT",0,offset) |
child:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,offset) |
child.offset = offset |
end |
end |
end |
function WaterfallTreeView.prototype:ToggleExpand(id) |
self.status[id] = not self.status[id] |
self:Refresh() |
end |
function WaterfallTreeView.prototype:Refresh(noupdate) |
self.noFixScroll = true |
self:Clear() |
if not self.contents then return end |
if self.contentsFunc and not noupdate then |
self.parent.controlcount = 0 |
currentframe = self.parent |
self.contentsFunc(self.contents, self.status, getArgs(self, "contentsArg", 1)) |
currentframe = nil |
end |
if self.type == "SECTIONS" then |
for i, section in ipairs(self.contents) do |
if not section.hidden then |
self:AddSection(i,section) |
end |
end |
local height = 0 |
for i, section in ipairs(self.sections) do |
height = height + section:GetHeight() |
end |
self.scrollchild:SetHeight(height+3) |
else |
local level = 0 |
local index = 1 |
for i, line in ipairs(self.contents) do |
if not line.hidden then |
index = self:AddLine(index,line,level) |
end |
end |
self.scrollchild:SetHeight(index*16) |
end |
self.noFixScroll = nil |
self:FixScroll() |
end |
local function isSectionSelected(section, selected) |
if not selected then return end |
if section.id == selected then |
return true |
end |
for i,v in ipairs(section.lines) do |
if i <= section.numlines then |
if v.id == selected then |
return true |
end |
end |
end |
end |
function WaterfallTreeView.prototype:Collapse() |
for k in pairs(self.status) do |
self.status[k] = false |
end |
end |
function WaterfallTreeView.prototype:AddSection(index,line) |
local sections = self.sections |
local id = line.id |
if not sections[index] then |
sections[index] = getObj(WaterfallTreeSection,self) |
local section = sections[index] |
section:SetParent(self) |
if index == 1 then |
section:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",0,0) |
section:SetPoint("TOPRIGHT",self.scrollchild,"TOPRIGHT",0,0) |
else |
section:SetPoint("TOPLEFT",sections[index-1],"BOTTOMLEFT",0,0) |
section:SetPoint("TOPRIGHT",sections[index-1],"BOTTOMRIGHT",0,0) |
end |
end |
local section = sections[index] |
section:SetText(line.text) |
section:SetID(line.id) |
section:SetTooltipText(line.tooltipTitle,line.tooltipText) |
if self.status[id] == nil then |
self.status[id] = not not line.isOpen |
end |
local handler = line.handler |
local disabled = checkNegatable(line.disabled,handler) |
section.disabled = disabled |
section:SetColor(self.parent:GetColor()) |
if line.hasOptions then |
section:AddLine(line.text,line.id,line.id == self.selected,disabled,line.tooltipTitle,line.tooltipText) |
end |
local firstid |
for i, item in ipairs(line) do |
if not item.hidden then |
if not firstid then |
firstid = item.id |
end |
local itemdisabled = checkNegatable(item.disabled,handler) |
section:AddLine(item.text,item.id,item.id == self.selected,itemdisabled or disabled,item.tooltipTitle,item.tooltipText) |
end |
end |
if self.selected == section.id and not line.hasOptions then |
self:SetSelected(firstid) |
return |
end |
if isSectionSelected(section, self.selected) then |
self.status[section.id] = true |
end |
section:SetExpanded(self.status[line.id] and not disabled) |
section:Show() |
end |
function WaterfallTreeView.prototype:Clear() |
for i, frame in ipairs(self.scrollchild.lines) do |
releaseObj(frame) |
self.scrollchild.lines[i] = nil |
end |
for k in pairs(self.linetables) do |
self.linetables[k] = nil |
end |
for i, section in ipairs(self.sections) do |
releaseObj(section) |
self.sections[i] = nil |
end |
self.numlines = 0 |
self.scrollchild:SetHeight(1) |
self:FixScroll() |
end |
function WaterfallTreeView.prototype:ClearSelection() |
self.selected = nil |
end |
function WaterfallTreeView.prototype:GetSelected() |
return self.selected |
end |
function WaterfallTreeView.prototype:SetSelected(lineid, noupdate) |
self.selected = lineid |
self:Refresh(noupdate) |
if not noupdate then |
self.parent:ChangePane(lineid) |
end |
end |
function WaterfallTreeView.prototype:AddLine(index,line,level,disabled) |
local lines = self.scrollchild.lines |
local text, hasChildren, isSelected, isOpen |
text = line.text or "" |
hasChildren = #line > 0 |
local id = line.id |
if self.status[id] == nil then |
self.status[id] = not not line.isOpen |
end |
isOpen = self.status[id] and not disabled |
isSelected = self.selected == id |
if not lines[index] then |
lines[index] = getObj(WaterfallTreeLine) |
lines[index]:SetParent(self) |
end |
local disabled |
if type(line.disabled) == "function" then |
disabled = line.disabled() |
else |
disabled = line.disabled |
end |
lines[index]:Setup(index,id,text,level,hasChildren,isSelected,isOpen,disabled) |
lines[index]:SetTooltipText(line.tooltipTitle,line.tooltipText) |
lines[index]:SetIcon(line.icon) |
self.linetables[index] = line |
index = index + 1 |
if isOpen then |
for i, childline in ipairs(line) do |
if not childline.hidden then |
index = self:AddLine(index,childline,level+1) |
end |
end |
end |
return index |
end |
function WaterfallTreeView.prototype:SetType(type) |
if not type then |
self.type = "TREE" |
else |
self.type = type |
end |
end |
function WaterfallTreeView.prototype:SetContent(contents, status, func, ...) |
self.contents = contents |
self.status = status |
self.contentsFunc = func |
local i = 1 |
while self["contentsArg"..i] do |
self["contentsArg"..i] = nil |
i = i + 1 |
end |
i = 1 |
local arg = (select(i,...)) |
while (select(i,...)) ~= nil do |
self["contentsArg"..i] = arg |
i = i + 1 |
arg = (select(i,...)) |
end |
self:Refresh() |
self:FixScroll() |
end |
function WaterfallTreeView.prototype:CleanUp() |
self.contents = nil |
self.selected = nil |
self.contentsFunc = nil |
local i = 1 |
while self["contentsArg"..i] do |
self["contentsArg"..i] = nil |
i = i + 1 |
end |
self.selected = nil |
self:Refresh() |
end |
-------------------- |
-- WaterfallTreeLine Class -- |
-------------------- |
local function TreeLine_OnEnter(this) |
if not this.obj.disabled then |
this.highlight:SetAlpha(1) |
this.highlight:Show() |
showGameTooltip(this.obj) |
end |
end |
local function TreeLine_OnLeave(this) |
if not this.obj.isSelected then |
this.highlight:Hide() |
else |
this.highlight:SetAlpha(0.7) |
end |
GameTooltip:Hide() |
end |
local function TreeLine_OnClick(this) |
if not this.obj.disabled then |
this.parent:SetSelected(this.obj.id) |
end |
end |
local function TreeLine_OnExpandClick(this) |
if not this.obj.disabled then |
this.obj.parent:ToggleExpand(this.obj.id) |
end |
end |
function WaterfallTreeLine.prototype:init() |
WaterfallTreeLine.super.prototype.init(self) |
local frame = CreateFrame("Button",nil,nil) |
self.frame = frame |
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") |
text:SetJustifyH("LEFT") |
text:SetTextColor(1,1,1) |
frame:SetHeight(16) |
frame:EnableMouse(true) |
frame:SetScript("OnEnter",TreeLine_OnEnter) |
frame:SetScript("OnLeave",TreeLine_OnLeave) |
frame:SetScript("OnClick",TreeLine_OnClick) |
frame:SetFrameLevel(10) |
frame:SetFrameStrata("DIALOG") |
local highlight = frame:CreateTexture(nil, "BACKGROUND") |
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") |
highlight:SetBlendMode("ADD") |
highlight:SetAllPoints(frame) |
highlight:Hide() |
local expand = CreateFrame("Button",nil,frame) |
expand.obj = self |
expand:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up") |
expand:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down") |
expand:SetDisabledTexture("Interface\\Buttons\\UI-PlusButton-Disabled") |
expand:SetHighlightTexture("Interface\\Buttons\\UI-PlusButton-Hilight") |
expand:SetScript("OnClick",TreeLine_OnExpandClick) |
expand:SetFrameLevel(21) |
expand:SetWidth(16) |
expand:SetHeight(16) |
local icon = frame:CreateTexture(nil,"OVERLAY") |
icon:SetWidth(16) |
icon:SetHeight(16) |
icon:SetPoint("LEFT",expand,"RIGHT",1,0) |
self.icon = icon |
expand:SetPoint("LEFT",frame,"LEFT",0,0) |
text:SetPoint("LEFT",icon,"RIGHT",0,0) |
text:SetPoint("RIGHT",frame,"RIGHT",0,0) |
frame.obj = self |
frame.text = text |
frame.highlight = highlight |
frame.expand = expand |
self.expand = expand |
self.highlight = highlight |
self.text = text |
end |
function WaterfallTreeLine.prototype:UpdateExpandButton() |
local expand = self.expand |
if self.hasChildren then |
if self.isOpen then |
expand:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up") |
expand:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-Down") |
expand:SetDisabledTexture("Interface\\Buttons\\UI-MinusButton-Disabled") |
expand:Enable() |
expand:SetWidth(16) |
else |
if self.disabled then |
expand:Disable() |
else |
expand:Enable() |
end |
expand:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up") |
expand:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down") |
expand:SetDisabledTexture("Interface\\Buttons\\UI-PlusButton-Disabled") |
expand:SetWidth(16) |
end |
else |
expand:SetNormalTexture(nil) |
expand:SetPushedTexture(nil) |
expand:SetDisabledTexture(nil) |
expand:Disable() |
expand:SetWidth(16) |
end |
end |
function WaterfallTreeLine.prototype:CleanUp() |
self:ClearAllPoints() |
self:Hide() |
self:SetText("") |
self.disabled = nil |
self.handler = nil |
self.name = nil |
end |
function WaterfallTreeLine.prototype:Setup(index, id, text, level, hasChildren, isSelected, isOpen, disabled) |
self.level = level |
self.hasChildren = hasChildren |
self.isSelected = isSelected |
self.isOpen = isOpen and not disabled |
self.index = index |
self.id = id |
self.disabled = disabled |
self:UpdateExpandButton() |
self.expand:SetPoint("LEFT",self.frame,"LEFT",level*8,0) |
self:SetText(text) |
if disabled then |
self.text:SetTextColor(0.5,0.5,0.5) |
else |
self.text:SetTextColor(1,1,1) |
end |
self.frame:SetPoint("TOPLEFT",self.parent.scrollchild,"TOPLEFT",0,-(index-1)*16) |
self.frame:SetPoint("TOPRIGHT",self.parent.scrollchild,"TOPRIGHT",0,-(index-1)*16) |
if self.isSelected then |
self.highlight:Show() |
self.highlight:SetAlpha(0.7) |
else |
self.highlight:Hide() |
end |
self.frame:Show() |
end |
function WaterfallTreeLine.prototype:SetIcon(iconpath) |
local icon = self.icon |
if iconpath then |
icon:SetTexture(iconpath) |
icon:SetWidth(16) |
else |
icon:SetTexture(nil) |
icon:SetWidth(1) |
end |
end |
function WaterfallTreeLine.prototype:SetText(text) |
self.text:SetText(text) |
end |
function WaterfallTreeLine.prototype:SetTooltipText(title,text) |
self.tooltipTitle = title |
self.tooltipText = text |
end |
function WaterfallTreeLine.prototype:SetParent(parent) |
self.parent = parent |
self.frame.parent = parent |
self.frame:SetParent(parent.scrollchild) |
end |
----------------------- |
-- TreeSection Class -- |
----------------------- |
local function SectionTitle_OnClick(this) |
local self = this.obj |
local parent = self.parent |
if not self.disabled then |
local selected = self.parent.selected |
local lines = self.lines |
if self.expanded then |
--search before changing pane, since it will release and reinit this section |
local found |
for i = 1, self.numlines do |
if lines[i].id == selected then |
found = true |
end |
end |
--if expanded close |
-- if one of our children is selected then clear the pane |
if found then |
parent.selected = nil |
parent:ToggleExpand(self.id) |
parent:SetSelected(nil) |
else |
parent:ToggleExpand(self.id) |
end |
else |
--save before changing pane, since it will release and reinit this section |
local newid |
if lines[1] then |
newid = lines[1].id |
end |
parent:ToggleExpand(self.id) |
if newid and not parent.selected then |
parent:SetSelected(newid) |
end |
end |
end |
end |
local function SectionTitle_OnEnter(this) |
local self = this.obj |
if not self.disabled then |
self.hover = true |
self:UpdateColor() |
end |
showGameTooltip(self) |
end |
local function SectionTitle_OnLeave(this) |
local self = this.obj |
self.hover = false |
self:UpdateColor() |
GameTooltip:Hide() |
end |
local function SectionItem_OnEnter(this) |
if not this.disabled then |
this.highlight:SetAlpha(1) |
this.highlight:Show() |
showGameTooltip(this) |
end |
end |
local function SectionItem_OnClick(this) |
if not this.disabled then |
this.obj.parent:SetSelected(this.id) |
end |
end |
local function SectionItem_OnLeave(this) |
if not this.isSelected then |
this.highlight:Hide() |
else |
this.highlight:SetAlpha(0.7) |
end |
GameTooltip:Hide() |
end |
function WaterfallTreeSection.prototype:init(parent) |
WaterfallTreeSection.super.prototype.init(self) |
self.parent = parent |
local frame = CreateFrame("Frame",nil,parent.scrollchild) |
frame.obj = self |
self.frame = frame |
frame:SetBackdrop(PaneBackdrop) |
frame:SetBackdropBorderColor(0.6, 0.6, 0.6) |
frame:SetBackdropColor(0.1, 0.1, 0.1) |
frame:SetFrameLevel(10) |
frame:SetFrameStrata("DIALOG") |
local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") |
self.titletext = titletext |
titletext:SetPoint("TOPLEFT",frame,"TOPLEFT",0,-5) |
titletext:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,-5) |
titletext:SetHeight(16) |
titletext:SetText("Section") |
titletext:SetTextColor(1,1,1) |
local arrow = frame:CreateTexture(nil,"OVERLAY") |
self.arrow = arrow |
arrow:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-6,-4) |
arrow:SetWidth(20) |
arrow:SetHeight(20) |
arrow:SetTexture("Interface\\MiniMap\\ROTATING-MINIMAPARROW") |
local r,g,b = 1, 0.6, 0 |
self.colorR = r |
self.colorG = g |
self.colorB = b |
local titlebar = frame:CreateTexture(nil,"BACKGROUND") |
self.titlebar = titlebar |
local titlebar2 = frame:CreateTexture(nil,"BACKGROUND") |
self.titlebar2 = titlebar2 |
titlebar:SetPoint("TOPLEFT",frame,"TOPLEFT",4,-4) |
titlebar:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-4,-4) |
titlebar:SetHeight(10) |
titlebar2:SetPoint("TOPLEFT",titlebar,"BOTTOMLEFT",0,0) |
titlebar2:SetPoint("TOPRIGHT",titlebar,"BOTTOMRIGHT",0,0) |
titlebar2:SetHeight(10) |
titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) |
titlebar:SetTexture(r,g,b,1) |
titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) |
titlebar2:SetTexture(r,g,b,1) |
local title = CreateFrame("Button",nil,frame) |
self.title = title |
title:EnableMouse() |
title.obj = self |
title:SetScript("OnClick", SectionTitle_OnClick) |
title:SetScript("OnEnter", SectionTitle_OnEnter) |
title:SetScript("OnLeave", SectionTitle_OnLeave) |
title:SetPoint("TOPLEFT",titlebar,"TOPLEFT",0,0) |
title:SetPoint("BOTTOMRIGHT",titlebar2,"BOTTOMRIGHT",0,0) |
self.lines = {} |
self.numlines = 0 |
frame:SetWidth(100) |
frame:SetHeight(100) |
self.expanded = true |
frame:SetPoint("CENTER",UIParent,"CENTER") |
end |
function WaterfallTreeSection.prototype:SetParent(parent) |
self.parent = parent |
self.frame:SetParent(parent.scrollchild) |
end |
function WaterfallTreeSection.prototype:UpdateColor() |
local r,g,b |
self.arrow:Show() |
if self.disabled then |
r,g,b = 0.5,0.5,0.5 |
self.arrow:Hide() |
elseif self.hover then |
r,g,b = self.colorR+((1-self.colorR)/2), self.colorG+((1-self.colorG)/2), self.colorB+((1-self.colorB)/2) |
else |
r,g,b = self.colorR, self.colorG, self.colorB |
end |
local titlebar = self.titlebar |
local titlebar2 = self.titlebar2 |
titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) |
titlebar:SetTexture(r,g,b,1) |
titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) |
titlebar2:SetTexture(r,g,b,1) |
end |
function WaterfallTreeSection.prototype:SetColor(r,g,b) |
if r and g and b then |
self.colorR, self.colorG, self.colorB = r,g,b |
end |
self:UpdateColor() |
end |
function WaterfallTreeSection.prototype:ToggleExpand() |
self:SetExpanded(not self.expanded) |
end |
function WaterfallTreeSection.prototype:SetText(text) |
self.titletext:SetText(text) |
end |
function WaterfallTreeSection.prototype:SetTooltipText(title,text) |
self.tooltipTitle = title |
self.tooltipText = text |
end |
function WaterfallTreeSection.prototype:SetID(id) |
self.id = id |
end |
function WaterfallTreeSection.prototype:AddLine(text,id,selected,disabled,tooltipTitle,tooltipText) |
local numlines = self.numlines + 1 |
self.numlines = numlines |
local lines = self.lines |
if not lines[numlines] then |
self:CreateLine(numlines) |
end |
local line = lines[numlines] |
line.text:SetText(text) |
line.id = id |
line.disabled = disabled |
line.isSelected = selected |
line.tooltipTitle = tooltipTitle |
line.tooltipText = tooltipText |
if selected then |
line.highlight:SetAlpha(0.7) |
line.highlight:Show() |
else |
line.highlight:Hide() |
end |
if disabled then |
line.text:SetTextColor(0.5,0.5,0.5) |
else |
line.text:SetTextColor(1,1,1) |
end |
line:Show() |
self:FixHeight() |
end |
function WaterfallTreeSection.prototype:SetExpanded(expanded) |
self.expanded = expanded |
local lines = self.lines |
if self.expanded then |
for i = 1, self.numlines do |
lines[i]:Show() |
end |
self.arrow:SetTexCoord(.18,.85,.15,.82) |
else |
for i = 1, #lines do |
lines[i]:Hide() |
end |
self.arrow:SetTexCoord(.85,.18,.82,.15) |
end |
self:FixHeight() |
end |
function WaterfallTreeSection.prototype:FixHeight() |
if self.expanded then |
self.frame:SetHeight(33 + self.numlines * 18) |
else |
self.frame:SetHeight(28) |
end |
end |
function WaterfallTreeSection.prototype:CreateLine(index) |
local frame = self.frame |
self.lines[index] = CreateFrame("Button",nil,frame) |
local line = self.lines[index] |
line.obj = self |
line:SetScript("OnEnter",SectionItem_OnEnter) |
line:SetScript("OnLeave",SectionItem_OnLeave) |
line:SetScript("OnClick",SectionItem_OnClick) |
line:SetPoint("LEFT",frame,"LEFT",5,0) |
line:SetPoint("RIGHT",frame,"RIGHT",-5,0) |
line:SetHeight(16) |
if index == 1 then |
line:SetPoint("TOP",frame,"TOP",0,-28) |
else |
line:SetPoint("TOP",self.lines[index-1],"BOTTOM",0,-2) |
end |
local text = line:CreateFontString(nil,"OVERLAY","GameFontNormal") |
line.text = text |
text:SetAllPoints(line) |
text:SetTextColor(1,1,1) |
local highlight = line:CreateTexture(nil, "BACKGROUND") |
line.highlight = highlight |
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") |
highlight:SetBlendMode("ADD") |
highlight:SetAllPoints(line) |
highlight:Hide() |
end |
function WaterfallTreeSection.prototype:CleanUp() |
for i = 1, #self.lines do |
self.lines[i]:Hide() |
end |
self.numlines = 0 |
self.frame:Hide() |
self.frame:ClearAllPoints() |
self.id = nil |
self.disabled = nil |
self.colorR = 1 |
self.colorG = 0.6 |
self.colorB = 0 |
end |
local function activate(self, oldLib, oldDeactivate) |
if oldLib and oldLib.registry then |
self.registry = oldLib.registry |
else |
self.registry = {} |
end |
if oldDeactivate then |
oldDeactivate(oldLib) |
end |
end |
AceLibrary:Register(Waterfall, MAJOR_VERSION, MINOR_VERSION, activate) |
Waterfall = AceLibrary("Waterfall-1.0") |
<?xml version="1.0" encoding="utf-8"?> |
<Ui xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.blizzard.com/wow/ui/" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<!-- Removed by files.wowace.com for no-ext zip. --></Ui> |
------------------------------------------------------------------------ |
r76021 | toadkiller | 2008-06-04 15:40:32 -0400 (Wed, 04 Jun 2008) | 2 lines |
Changed paths: |
M /trunk/Waterfall-1.0/Waterfall-1.0/Waterfall-1.0.lua |
Waterfall-1.0 : |
-- Use English name for spell CLASS. |
------------------------------------------------------------------------ |
## Interface: 20400 |
## LoadOnDemand: 1 |
## Title: Lib: Waterfall-1.0 |
## Notes: GUI Configuration Library |
## Author: Nargiddley |
## eMail: nargiddley@gmail.com |
## X-Category: Library |
## OptionalDeps: Ace2 |
## X-AceLibrary-Waterfall-1.0: true |
## X-License: LGPL v2.1 |
embeds.xml |
Waterfall-1.0\Waterfall-1.0.lua |
--[[ |
Name: AceEvent-2.0 |
Revision: $Rev: 66043 $ |
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) |
Inspired By: Ace 1.x by Turan (turan@gryphon.com) |
Website: http://www.wowace.com/ |
Documentation: http://www.wowace.com/index.php/AceEvent-2.0 |
SVN: http://svn.wowace.com/root/trunk/Ace2/AceEvent-2.0 |
Description: Mixin to allow for event handling, scheduling, and inter-addon |
communication. |
Dependencies: AceLibrary, AceOO-2.0 |
License: LGPL v2.1 |
]] |
local MAJOR_VERSION = "AceEvent-2.0" |
local MINOR_VERSION = "$Revision: 66043 $" |
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end |
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end |
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end |
local AceOO = AceLibrary:GetInstance("AceOO-2.0") |
local Mixin = AceOO.Mixin |
local AceEvent = Mixin { |
"RegisterEvent", |
"RegisterAllEvents", |
"UnregisterEvent", |
"UnregisterAllEvents", |
"TriggerEvent", |
"ScheduleEvent", |
"ScheduleRepeatingEvent", |
"CancelScheduledEvent", |
"CancelAllScheduledEvents", |
"IsEventRegistered", |
"IsEventScheduled", |
"RegisterBucketEvent", |
"UnregisterBucketEvent", |
"UnregisterAllBucketEvents", |
"IsBucketEventRegistered", |
"ScheduleLeaveCombatAction", |
"CancelAllCombatSchedules", |
} |
local weakKey = {__mode="k"} |
local FAKE_NIL |
local RATE |
local eventsWhichHappenOnce = { |
PLAYER_LOGIN = true, |
AceEvent_FullyInitialized = true, |
VARIABLES_LOADED = true, |
PLAYER_LOGOUT = true, |
} |
local next = next |
local pairs = pairs |
local pcall = pcall |
local type = type |
local GetTime = GetTime |
local gcinfo = gcinfo |
local unpack = unpack |
local geterrorhandler = geterrorhandler |
local new, del |
do |
local cache = setmetatable({}, {__mode='k'}) |
function new(...) |
local t = next(cache) |
if t then |
cache[t] = nil |
for i = 1, select('#', ...) do |
t[i] = select(i, ...) |
end |
return t |
else |
return { ... } |
end |
end |
function del(t) |
for k in pairs(t) do |
t[k] = nil |
end |
cache[t] = true |
return nil |
end |
end |
local registeringFromAceEvent |
--[[---------------------------------------------------------------------------------- |
Notes: |
* Registers the addon with a Blizzard event or a custom AceEvent, which will cause the given method to be called when that is triggered. |
Arguments: |
string - name of the event to register |
[optional] string or function - name of the method or function to call. Default: same name as "event". |
[optional] boolean - whether to have method called only once. Default: false |
------------------------------------------------------------------------------------]] |
function AceEvent:RegisterEvent(event, method, once) |
AceEvent:argCheck(event, 2, "string") |
if self == AceEvent and not registeringFromAceEvent then |
AceEvent:argCheck(method, 3, "function") |
self = method |
else |
AceEvent:argCheck(method, 3, "string", "function", "nil", "boolean", "number") |
if type(method) == "boolean" or type(method) == "number" then |
AceEvent:argCheck(once, 4, "nil") |
once, method = method, event |
end |
end |
AceEvent:argCheck(once, 4, "number", "boolean", "nil") |
if eventsWhichHappenOnce[event] then |
once = true |
end |
local throttleRate |
if type(once) == "number" then |
throttleRate, once = once |
end |
if not method then |
method = event |
end |
if type(method) == "string" and type(self[method]) ~= "function" then |
AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method) |
else |
assert(type(method) == "function" or type(method) == "string") |
end |
local AceEvent_registry = AceEvent.registry |
if not AceEvent_registry[event] then |
AceEvent_registry[event] = new() |
AceEvent.frame:RegisterEvent(event) |
end |
local remember = true |
if AceEvent_registry[event][self] then |
remember = false |
end |
AceEvent_registry[event][self] = method |
local AceEvent_onceRegistry = AceEvent.onceRegistry |
if once then |
if not AceEvent_onceRegistry then |
AceEvent.onceRegistry = {} |
AceEvent_onceRegistry = AceEvent.onceRegistry |
end |
if not AceEvent_onceRegistry[event] then |
AceEvent_onceRegistry[event] = new() |
end |
AceEvent_onceRegistry[event][self] = true |
else |
if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then |
AceEvent_onceRegistry[event][self] = nil |
if not next(AceEvent_onceRegistry[event]) then |
AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event]) |
end |
end |
end |
local AceEvent_throttleRegistry = AceEvent.throttleRegistry |
if throttleRate then |
if not AceEvent_throttleRegistry then |
AceEvent.throttleRegistry = {} |
AceEvent_throttleRegistry = AceEvent.throttleRegistry |
end |
if not AceEvent_throttleRegistry[event] then |
AceEvent_throttleRegistry[event] = new() |
end |
if AceEvent_throttleRegistry[event][self] then |
AceEvent_throttleRegistry[event][self] = nil |
end |
AceEvent_throttleRegistry[event][self] = setmetatable(new(), weakKey) |
local t = AceEvent_throttleRegistry[event][self] |
t[RATE] = throttleRate |
else |
if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] then |
if AceEvent_throttleRegistry[event][self] then |
AceEvent_throttleRegistry[event][self] = nil |
end |
if not next(AceEvent_throttleRegistry[event]) then |
AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event]) |
end |
end |
end |
if remember then |
AceEvent:TriggerEvent("AceEvent_EventRegistered", self, event) |
end |
end |
local ALL_EVENTS |
--[[---------------------------------------------------------------------------------- |
Notes: |
* Registers all events to the given method |
* To access the current event, check AceEvent.currentEvent |
* To access the current event's unique identifier, check AceEvent.currentEventUID |
* This is only for debugging purposes. |
Arguments: |
[optional] string or function - name of the method or function to call. Default: same name as "event". |
------------------------------------------------------------------------------------]] |
function AceEvent:RegisterAllEvents(method) |
if self == AceEvent then |
AceEvent:argCheck(method, 1, "function") |
self = method |
else |
AceEvent:argCheck(method, 1, "string", "function") |
if type(method) == "string" and type(self[method]) ~= "function" then |
AceEvent:error("Cannot register all events to method %q, it does not exist", method) |
end |
end |
local AceEvent_registry = AceEvent.registry |
if not AceEvent_registry[ALL_EVENTS] then |
AceEvent_registry[ALL_EVENTS] = new() |
AceEvent.frame:RegisterAllEvents() |
end |
local remember = not AceEvent_registry[ALL_EVENTS][self] |
AceEvent_registry[ALL_EVENTS][self] = method |
if remember then |
AceEvent:TriggerEvent("AceEvent_EventRegistered", self, "all") |
end |
end |
--[[---------------------------------------------------------------------------------- |
Notes: |
* Trigger a custom AceEvent. |
* This should never be called to simulate fake Blizzard events. |
* Custom events should be in the form of AddonName_SpecificEvent |
Arguments: |
string - name of the event |
tuple - list of arguments to pass along |
------------------------------------------------------------------------------------]] |
function AceEvent:TriggerEvent(event, ...) |
AceEvent:argCheck(event, 2, "string") |
local AceEvent_registry = AceEvent.registry |
if (not AceEvent_registry[event] or not next(AceEvent_registry[event])) and (not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS])) then |
return |
end |
local lastEvent = AceEvent.currentEvent |
AceEvent.currentEvent = event |
local lastEventUID = AceEvent.currentEventUID |
local uid = AceEvent.UID_NUM + 1 |
AceEvent.UID_NUM = uid |
AceEvent.currentEventUID = uid |
local tmp = new() |
local AceEvent_onceRegistry = AceEvent.onceRegistry |
if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then |
for obj, method in pairs(AceEvent_onceRegistry[event]) do |
tmp[obj] = AceEvent_registry[event] and AceEvent_registry[event][obj] or nil |
end |
local obj = next(tmp) |
while obj do |
local method = tmp[obj] |
AceEvent.UnregisterEvent(obj, event) |
if type(method) == "string" then |
local obj_method = obj[method] |
if obj_method then |
local success, err = pcall(obj_method, obj, ...) |
if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end |
end |
elseif method then -- function |
local success, err = pcall(method, ...) |
if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end |
end |
tmp[obj] = nil |
obj = next(tmp) |
end |
end |
local AceEvent_throttleRegistry = AceEvent.throttleRegistry |
local throttleTable = AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] |
if AceEvent_registry[event] then |
for obj, method in pairs(AceEvent_registry[event]) do |
tmp[obj] = method |
end |
local obj = next(tmp) |
while obj do |
local continue = nil |
if throttleTable and throttleTable[obj] then |
local a1 = ... |
if a1 == nil then |
a1 = FAKE_NIL |
end |
if not throttleTable[obj][a1] or GetTime() - throttleTable[obj][a1] >= throttleTable[obj][RATE] then |
throttleTable[obj][a1] = GetTime() |
else |
continue = true |
end |
end |
if not continue then |
local method = tmp[obj] |
local t = type(method) |
if t == "string" then |
local obj_method = obj[method] |
if obj_method then |
local success, err = pcall(obj_method, obj, ...) |
if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end |
end |
elseif t == "function" then -- function |
local success, err = pcall(method, ...) |
if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end |
else |
AceEvent:error("Cannot trigger event %q. %q's handler, %q, is not a method or function (%s).", event, obj, method, t) |
end |
end |
tmp[obj] = nil |
obj = next(tmp) |
end |
end |
if AceEvent_registry[ALL_EVENTS] then |
for obj, method in pairs(AceEvent_registry[ALL_EVENTS]) do |
tmp[obj] = method |
end |
local obj = next(tmp) |
while obj do |
local method = tmp[obj] |
local t = type(method) |
if t == "string" then |
local obj_method = obj[method] |
if obj_method then |
local success, err = pcall(obj_method, obj, ...) |
if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end |
end |
elseif t == "function" then |
local success, err = pcall(method, ...) |
if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end |
else |
AceEvent:error("Cannot trigger event %q. %q's handler, %q, is not a method or function (%s).", event, obj, method, t) |
end |
tmp[obj] = nil |
obj = next(tmp) |
end |
end |
tmp = del(tmp) |
AceEvent.currentEvent = lastEvent |
AceEvent.currentEventUID = lastEventUID |
end |
local delayRegistry |
local OnUpdate |
do |
local tmp = {} |
OnUpdate = function() |
local t = GetTime() |
for k,v in pairs(delayRegistry) do |
tmp[k] = true |
end |
for k in pairs(tmp) do |
local v = delayRegistry[k] |
if v then |
local v_time = v.time |
if not v_time then |
delayRegistry[k] = nil |
elseif v_time <= t then |
local v_repeatDelay = v.repeatDelay |
if v_repeatDelay then |
-- use the event time, not the current time, else timing inaccuracies add up over time |
v.time = v_time + v_repeatDelay |
end |
local event = v.event |
local t = type(event) |
if t == "function" then |
local uid = AceEvent.UID_NUM + 1 |
AceEvent.UID_NUM = uid |
AceEvent.currentEventUID = uid |
local success, err = pcall(event, unpack(v, 1, v.n)) |
if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end |
AceEvent.currentEventUID = nil |
elseif t == "string" then |
AceEvent:TriggerEvent(event, unpack(v, 1, v.n)) |
else |
AceEvent:error("Cannot trigger event %q, it's not a method or function (%s).", event, t) |
end |
if not v_repeatDelay then |
local x = delayRegistry[k] |
if x and x.time == v_time then -- check if it was manually reset |
if type(k) == "string" then |
del(delayRegistry[k]) |
end |
delayRegistry[k] = nil |
end |
end |
end |
end |
end |
for k in pairs(tmp) do |
tmp[k] = nil |
end |
if not next(delayRegistry) then |
AceEvent.frame:Hide() |
end |
end |
end |
local function ScheduleEvent(self, repeating, event, delay, ...) |
local id |
if type(event) == "string" and type(delay) ~= "number" then |
id, event, delay = event, delay, ... |
AceEvent:argCheck(event, 3, "string", "function", --[[ so message is right ]] "number") |
AceEvent:argCheck(delay, 4, "number") |
self:CancelScheduledEvent(id) |
else |
AceEvent:argCheck(event, 2, "string", "function") |
AceEvent:argCheck(delay, 3, "number") |
end |
if not delayRegistry then |
AceEvent.delayRegistry = {} |
delayRegistry = AceEvent.delayRegistry |
AceEvent.frame:SetScript("OnUpdate", OnUpdate) |
end |
local t |
if id then |
t = new(select(2, ...)) |
t.n = select('#', ...) - 1 |
else |
t = new(...) |
t.n = select('#', ...) |
end |
t.event = event |
t.time = GetTime() + delay |
t.self = self |
t.id = id or t |
t.repeatDelay = repeating and delay |
delayRegistry[t.id] = t |
AceEvent.frame:Show() |
end |
--[[---------------------------------------------------------------------------------- |
Notes: |
* Schedule an event to fire. |
* To fire on the next frame, specify a delay of 0. |
Arguments: |
string or function - name of the event to fire, or a function to call. |
number - the amount of time to wait until calling. |
tuple - a list of arguments to pass along. |
------------------------------------------------------------------------------------]] |
function AceEvent:ScheduleEvent(event, delay, ...) |
if type(event) == "string" and type(delay) ~= "number" then |
AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number") |
AceEvent:argCheck(..., 4, "number") |
else |
AceEvent:argCheck(event, 2, "string", "function") |
AceEvent:argCheck(delay, 3, "number") |
end |
return ScheduleEvent(self, false, event, delay, ...) |
end |
function AceEvent:ScheduleRepeatingEvent(event, delay, ...) |
if type(event) == "string" and type(delay) ~= "number" then |
AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number") |
AceEvent:argCheck(..., 4, "number") |
else |
AceEvent:argCheck(event, 2, "string", "function") |
AceEvent:argCheck(delay, 3, "number") |
end |
return ScheduleEvent(self, true, event, delay, ...) |
end |
function AceEvent:CancelScheduledEvent(t) |
AceEvent:argCheck(t, 2, "string") |
if delayRegistry then |
local v = delayRegistry[t] |
if v then |
if type(t) == "string" then |
del(delayRegistry[t]) |
end |
delayRegistry[t] = nil |
if not next(delayRegistry) then |
AceEvent.frame:Hide() |
end |
return true |
end |
end |
return false |
end |
function AceEvent:IsEventScheduled(t) |
AceEvent:argCheck(t, 2, "string") |
if delayRegistry then |
local v = delayRegistry[t] |
if v then |
return true, v.time - GetTime() |
end |
end |
return false, nil |
end |
function AceEvent:UnregisterEvent(event) |
AceEvent:argCheck(event, 2, "string") |
local AceEvent_registry = AceEvent.registry |
if AceEvent_registry[event] and AceEvent_registry[event][self] then |
AceEvent_registry[event][self] = nil |
local AceEvent_onceRegistry = AceEvent.onceRegistry |
if AceEvent_onceRegistry and AceEvent_onceRegistry[event] and AceEvent_onceRegistry[event][self] then |
AceEvent_onceRegistry[event][self] = nil |
if not next(AceEvent_onceRegistry[event]) then |
AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event]) |
end |
end |
local AceEvent_throttleRegistry = AceEvent.throttleRegistry |
if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] and AceEvent_throttleRegistry[event][self] then |
AceEvent_throttleRegistry[event][self] = nil |
if not next(AceEvent_throttleRegistry[event]) then |
AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event]) |
end |
end |
if not next(AceEvent_registry[event]) then |
AceEvent_registry[event] = del(AceEvent_registry[event]) |
if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then |
AceEvent.frame:UnregisterEvent(event) |
end |
end |
else |
if self == AceEvent then |
error(("Cannot unregister event %q. Improperly unregistering from AceEvent-2.0."):format(event), 2) |
else |
AceEvent:error("Cannot unregister event %q. %q is not registered with it.", event, self) |
end |
end |
AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) |
end |
function AceEvent:UnregisterAllEvents() |
local AceEvent_registry = AceEvent.registry |
if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then |
AceEvent_registry[ALL_EVENTS][self] = nil |
if not next(AceEvent_registry[ALL_EVENTS]) then |
AceEvent_registry[ALL_EVENTS] = del(AceEvent_registry[ALL_EVENTS]) |
AceEvent.frame:UnregisterAllEvents() |
for k,v in pairs(AceEvent_registry) do |
AceEvent.frame:RegisterEvent(k) |
end |
end |
end |
if AceEvent_registry.AceEvent_EventUnregistered then |
local event, data = "AceEvent_EventUnregistered", AceEvent_registry.AceEvent_EventUnregistered |
local x = data[self] |
data[self] = nil |
if x then |
if not next(data) then |
if not AceEvent_registry[ALL_EVENTS] then |
AceEvent.frame:UnregisterEvent(event) |
end |
AceEvent_registry[event] = del(AceEvent_registry[event]) |
end |
AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) |
end |
end |
for event, data in pairs(AceEvent_registry) do |
local x = data[self] |
data[self] = nil |
if x and event ~= ALL_EVENTS then |
if not next(data) then |
if not AceEvent_registry[ALL_EVENTS] then |
AceEvent.frame:UnregisterEvent(event) |
end |
AceEvent_registry[event] = del(AceEvent_registry[event]) |
end |
AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) |
end |
end |
if AceEvent.onceRegistry then |
for event, data in pairs(AceEvent.onceRegistry) do |
data[self] = nil |
end |
end |
end |
function AceEvent:CancelAllScheduledEvents() |
if delayRegistry then |
for k,v in pairs(delayRegistry) do |
if v.self == self then |
if type(k) == "string" then |
del(delayRegistry[k]) |
end |
delayRegistry[k] = nil |
end |
end |
if not next(delayRegistry) then |
AceEvent.frame:Hide() |
end |
end |
end |
function AceEvent:IsEventRegistered(event) |
AceEvent:argCheck(event, 2, "string") |
local AceEvent_registry = AceEvent.registry |
if self == AceEvent then |
return AceEvent_registry[event] and next(AceEvent_registry[event]) or AceEvent_registry[ALL_EVENTS] and next(AceEvent_registry[ALL_EVENTS]) and true or false |
end |
if AceEvent_registry[event] and AceEvent_registry[event][self] then |
return true, AceEvent_registry[event][self] |
end |
if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then |
return true, AceEvent_registry[ALL_EVENTS][self] |
end |
return false, nil |
end |
local UnitExists = UnitExists |
local bucketfunc |
function AceEvent:RegisterBucketEvent(event, delay, method, ...) |
AceEvent:argCheck(event, 2, "string", "table") |
if type(event) == "table" then |
for k,v in pairs(event) do |
if type(k) ~= "number" then |
AceEvent:error("All keys to argument #2 to `RegisterBucketEvent' must be numbers.") |
elseif type(v) ~= "string" then |
AceEvent:error("All values to argument #2 to `RegisterBucketEvent' must be strings.") |
end |
end |
end |
AceEvent:argCheck(delay, 3, "number") |
if AceEvent == self then |
AceEvent:argCheck(method, 4, "function") |
self = method |
else |
if type(event) == "string" then |
AceEvent:argCheck(method, 4, "string", "function", "nil") |
if not method then |
method = event |
end |
else |
AceEvent:argCheck(method, 4, "string", "function") |
end |
if type(method) == "string" and type(self[method]) ~= "function" then |
AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method) |
end |
end |
local buckets = AceEvent.buckets |
if not buckets[event] then |
buckets[event] = new() |
end |
if not buckets[event][self] then |
local t = {} |
t.current = {} |
t.self = self |
buckets[event][self] = t |
else |
AceEvent.CancelScheduledEvent(self, buckets[event][self].id) |
end |
local bucket = buckets[event][self] |
bucket.method = method |
local n = select('#', ...) |
if n > 0 then |
for i = 1, n do |
bucket[i] = select(i, ...) |
end |
end |
bucket.n = n |
local func = function(arg1) |
bucket.run = true |
if arg1 then |
bucket.current[arg1] = true |
end |
end |
buckets[event][self].func = func |
local isUnitBucket = true |
if type(event) == "string" then |
AceEvent.RegisterEvent(self, event, func) |
if not event:find("^UNIT_") then |
isUnitBucket = nil |
end |
else |
for _,v in ipairs(event) do |
AceEvent.RegisterEvent(self, v, func) |
if isUnitBucket and not v:find("^UNIT_") then |
isUnitBucket = nil |
end |
end |
end |
bucket.unit = isUnitBucket |
if not bucketfunc then |
bucketfunc = function(bucket) |
if bucket.run then |
local current = bucket.current |
local method = bucket.method |
local self = bucket.self |
if bucket.unit then |
for unit in pairs(current) do |
if not UnitExists(unit) then |
current[unit] = nil |
end |
end |
end |
if type(method) == "string" then |
self[method](self, current, unpack(bucket, 1, bucket.n)) |
elseif method then -- function |
method(current, unpack(bucket, 1, bucket.n)) |
end |
for k in pairs(current) do |
current[k] = nil |
k = nil |
end |
bucket.run = nil |
end |
end |
end |
bucket.id = "AceEvent-Bucket-" .. tostring(bucket) |
AceEvent.ScheduleRepeatingEvent(self, bucket.id, bucketfunc, delay, bucket) |
end |
function AceEvent:IsBucketEventRegistered(event) |
AceEvent:argCheck(event, 2, "string", "table") |
return AceEvent.buckets and AceEvent.buckets[event] and AceEvent.buckets[event][self] |
end |
function AceEvent:UnregisterBucketEvent(event) |
AceEvent:argCheck(event, 2, "string", "table") |
if not AceEvent.buckets or not AceEvent.buckets[event] or not AceEvent.buckets[event][self] then |
AceEvent:error("Cannot unregister bucket event %q. %q is not registered with it.", event, self) |
end |
local bucket = AceEvent.buckets[event][self] |
if type(event) == "string" then |
AceEvent.UnregisterEvent(self, event) |
else |
for _,v in ipairs(event) do |
AceEvent.UnregisterEvent(self, v) |
end |
end |
AceEvent:CancelScheduledEvent(bucket.id) |
bucket.current = nil |
AceEvent.buckets[event][self] = nil |
if not next(AceEvent.buckets[event]) then |
AceEvent.buckets[event] = del(AceEvent.buckets[event]) |
end |
end |
function AceEvent:UnregisterAllBucketEvents() |
if not AceEvent.buckets or not next(AceEvent.buckets) then |
return |
end |
for k,v in pairs(AceEvent.buckets) do |
if v == self then |
AceEvent.UnregisterBucketEvent(self, k) |
k = nil |
end |
end |
end |
local combatSchedules |
function AceEvent:CancelAllCombatSchedules() |
local i = 0 |
while true do |
i = i + 1 |
if not combatSchedules[i] then |
break |
end |
local v = combatSchedules[i] |
if v.self == self then |
v = del(v) |
table.remove(combatSchedules, i) |
i = i - 1 |
end |
end |
end |
local inCombat = false |
function AceEvent:PLAYER_REGEN_DISABLED() |
inCombat = true |
end |
do |
local tmp = {} |
function AceEvent:PLAYER_REGEN_ENABLED() |
inCombat = false |
for i, v in ipairs(combatSchedules) do |
tmp[i] = v |
combatSchedules[i] = nil |
end |
for i, v in ipairs(tmp) do |
local func = v.func |
if func then |
local success, err = pcall(func, unpack(v, 1, v.n)) |
if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end |
else |
local obj = v.obj or v.self |
local method = v.method |
local obj_method = obj[method] |
if obj_method then |
local success, err = pcall(obj_method, obj, unpack(v, 1, v.n)) |
if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end |
end |
end |
tmp[i] = del(v) |
end |
end |
end |
function AceEvent:ScheduleLeaveCombatAction(method, ...) |
local style = type(method) |
if self == AceEvent then |
if style == "table" then |
local func = (...) |
AceEvent:argCheck(func, 3, "string") |
if type(method[func]) ~= "function" then |
AceEvent:error("Cannot schedule a combat action to method %q, it does not exist", func) |
end |
else |
AceEvent:argCheck(method, 2, "function", --[[so message is right]] "table") |
end |
self = method |
else |
AceEvent:argCheck(method, 2, "function", "string", "table") |
if style == "string" and type(self[method]) ~= "function" then |
AceEvent:error("Cannot schedule a combat action to method %q, it does not exist", method) |
elseif style == "table" then |
local func = (...) |
AceEvent:argCheck(func, 3, "string") |
if type(method[func]) ~= "function" then |
AceEvent:error("Cannot schedule a combat action to method %q, it does not exist", func) |
end |
end |
end |
if not inCombat then |
local success, err |
if type(method) == "function" then |
success, err = pcall(method, ...) |
elseif type(method) == "table" then |
local func = (...) |
success, err = pcall(method[func], method, select(2, ...)) |
else |
success, err = pcall(self[method], self, ...) |
end |
if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end |
return |
end |
local t |
local n = select('#', ...) |
if style == "table" then |
t = new(select(2, ...)) |
t.obj = method |
t.method = (...) |
t.n = n-1 |
else |
t = new(...) |
t.n = n |
if style == "function" then |
t.func = method |
else |
t.method = method |
end |
end |
t.self = self |
table.insert(combatSchedules, t) |
end |
function AceEvent:OnEmbedDisable(target) |
self.UnregisterAllEvents(target) |
self.CancelAllScheduledEvents(target) |
self.UnregisterAllBucketEvents(target) |
self.CancelAllCombatSchedules(target) |
end |
function AceEvent:IsFullyInitialized() |
return self.postInit or false |
end |
local function activate(self, oldLib, oldDeactivate) |
AceEvent = self |
self.onceRegistry = oldLib and oldLib.onceRegistry or {} |
self.throttleRegistry = oldLib and oldLib.throttleRegistry or {} |
self.delayRegistry = oldLib and oldLib.delayRegistry or {} |
self.buckets = oldLib and oldLib.buckets or {} |
self.registry = oldLib and oldLib.registry or {} |
self.frame = oldLib and oldLib.frame or CreateFrame("Frame", "AceEvent20Frame") |
self.playerLogin = IsLoggedIn() and true |
self.postInit = oldLib and oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true |
self.ALL_EVENTS = oldLib and oldLib.ALL_EVENTS or _G.newproxy() |
self.FAKE_NIL = oldLib and oldLib.FAKE_NIL or _G.newproxy() |
self.RATE = oldLib and oldLib.RATE or _G.newproxy() |
self.combatSchedules = oldLib and oldLib.combatSchedules or {} |
self.UID_NUM = oldLib and oldLib.UID_NUM or 0 |
combatSchedules = self.combatSchedules |
ALL_EVENTS = self.ALL_EVENTS |
FAKE_NIL = self.FAKE_NIL |
RATE = self.RATE |
local inPlw = false |
local blacklist = { |
UNIT_INVENTORY_CHANGED = true, |
BAG_UPDATE = true, |
ITEM_LOCK_CHANGED = true, |
ACTIONBAR_SLOT_CHANGED = true, |
} |
self.frame:SetScript("OnEvent", function(_, event, ...) |
if event == "PLAYER_ENTERING_WORLD" then |
inPlw = false |
elseif event == "PLAYER_LEAVING_WORLD" then |
inPlw = true |
end |
if event and (not inPlw or not blacklist[event]) then |
self:TriggerEvent(event, ...) |
end |
end) |
if self.delayRegistry then |
delayRegistry = self.delayRegistry |
self.frame:SetScript("OnUpdate", OnUpdate) |
end |
self:UnregisterAllEvents() |
self:CancelAllScheduledEvents() |
local function handleFullInit() |
if not self.postInit then |
local function func() |
self.postInit = true |
self:TriggerEvent("AceEvent_FullyInitialized") |
if self.registry["CHAT_MSG_CHANNEL_NOTICE"] and self.registry["CHAT_MSG_CHANNEL_NOTICE"][self] then |
self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE") |
end |
if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then |
self:UnregisterEvent("MEETINGSTONE_CHANGED") |
end |
end |
registeringFromAceEvent = true |
self:RegisterEvent("MEETINGSTONE_CHANGED", func, true) |
self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", func, true) |
self:ScheduleEvent("AceEvent_FullyInitialized", func, 10) |
registeringFromAceEvent = nil |
end |
end |
if not self.playerLogin then |
registeringFromAceEvent = true |
self:RegisterEvent("PLAYER_LOGIN", function() |
self.playerLogin = true |
handleFullInit() |
handleFullInit = nil |
end, true) |
registeringFromAceEvent = nil |
else |
handleFullInit() |
handleFullInit = nil |
end |
if not AceEvent20EditBox then |
CreateFrame("Editbox", "AceEvent20EditBox") |
end |
local editbox = AceEvent20EditBox |
function editbox:Execute(line) |
local defaulteditbox = DEFAULT_CHAT_FRAME.editBox |
self:SetAttribute("chatType", defaulteditbox:GetAttribute("chatType")) |
self:SetAttribute("tellTarget", defaulteditbox:GetAttribute("tellTarget")) |
self:SetAttribute("channelTarget", defaulteditbox:GetAttribute("channelTarget")) |
self:SetText(line) |
ChatEdit_SendText(self) |
end |
editbox:Hide() |
_G["SLASH_IN1"] = "/in" |
SlashCmdList["IN"] = function(msg) |
local seconds, command, rest = msg:match("^([^%s]+)%s+(/[^%s]+)(.*)$") |
seconds = tonumber(seconds) |
if not seconds then |
DEFAULT_CHAT_FRAME:AddMessage("Error, bad arguments to /in. Must be in the form of `/in 5 /say hi'") |
return |
end |
if IsSecureCmd(command) then |
DEFAULT_CHAT_FRAME:AddMessage(("Error, /in cannot call secure command: %s"):format(command)) |
return |
end |
self:ScheduleEvent("AceEventSlashIn-" .. math.random(1, 1000000000), editbox.Execute, seconds, editbox, command .. rest) |
end |
registeringFromAceEvent = true |
self:RegisterEvent("PLAYER_REGEN_ENABLED") |
self:RegisterEvent("PLAYER_REGEN_DISABLED") |
self:RegisterEvent("LOOT_OPENED", function() |
SendAddonMessage("LOOT_OPENED", "", "RAID") |
end) |
inCombat = InCombatLockdown() |
registeringFromAceEvent = nil |
self:activate(oldLib, oldDeactivate) |
if oldLib then |
oldDeactivate(oldLib) |
end |
end |
AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, activate) |
## Interface: 20400 |
## Title: Lib: AceEvent-2.0 |
## Notes: AddOn development framework |
## Author: Ace Development Team |
## LoadOnDemand: 1 |
## X-Website: http://www.wowace.com |
## X-Category: Library |
## X-License: LGPL v2.1 + MIT for AceOO-2.0 |
## Dependencies: AceLibrary, AceOO-2.0 |
AceEvent-2.0.lua |
--[[ |
Name: AceConsole-2.0 |
Revision: $Rev: 67789 $ |
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) |
Inspired By: Ace 1.x by Turan (turan@gryphon.com) |
Website: http://www.wowace.com/ |
Documentation: http://www.wowace.com/index.php/AceConsole-2.0 |
SVN: http://svn.wowace.com/wowace/trunk/Ace2/AceConsole-2.0 |
Description: Mixin to allow for input/output capabilities. This uses the |
AceOptions data table format to determine input. |
http://www.wowace.com/index.php/AceOptions_data_table |
Dependencies: AceLibrary, AceOO-2.0 |
License: LGPL v2.1 |
]] |
local MAJOR_VERSION = "AceConsole-2.0" |
local MINOR_VERSION = "$Revision: 67789 $" |
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end |
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end |
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end |
-- #AUTODOC_NAMESPACE AceConsole |
local MAP_ONOFF, USAGE, IS_CURRENTLY_SET_TO, IS_NOW_SET_TO, IS_NOT_A_VALID_OPTION_FOR, IS_NOT_A_VALID_VALUE_FOR, NO_OPTIONS_AVAILABLE, OPTION_HANDLER_NOT_FOUND, OPTION_HANDLER_NOT_VALID, OPTION_IS_DISABLED, KEYBINDING_USAGE, DEFAULT_CONFIRM_MESSAGE |
if GetLocale() == "deDE" then |
MAP_ONOFF = { [false] = "|cffff0000Aus|r", [true] = "|cff00ff00An|r" } |
USAGE = "Benutzung" |
IS_CURRENTLY_SET_TO = "|cffffff7f%s|r steht momentan auf |cffffff7f[|r%s|cffffff7f]|r" |
IS_NOW_SET_TO = "|cffffff7f%s|r ist nun auf |cffffff7f[|r%s|cffffff7f]|r gesetzt" |
IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] ist keine g\195\188ltige Option f\195\188r |cffffff7f%s|r" |
IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] ist kein g\195\188ltiger Wert f\195\188r |cffffff7f%s|r" |
NO_OPTIONS_AVAILABLE = "Keine Optionen verfügbar" |
OPTION_HANDLER_NOT_FOUND = "Optionen handler |cffffff7f%q|r nicht gefunden." |
OPTION_HANDLER_NOT_VALID = "Optionen handler nicht g\195\188ltig." |
OPTION_IS_DISABLED = "Option |cffffff7f%s|r deaktiviert." |
KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>" -- fix |
DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?" -- fix |
elseif GetLocale() == "frFR" then |
MAP_ONOFF = { [false] = "|cffff0000Inactif|r", [true] = "|cff00ff00Actif|r" } |
USAGE = "Utilisation" |
IS_CURRENTLY_SET_TO = "|cffffff7f%s|r est actuellement positionn\195\169 sur |cffffff7f[|r%s|cffffff7f]|r" |
IS_NOW_SET_TO = "|cffffff7f%s|r est maintenant positionn\195\169 sur |cffffff7f[|r%s|cffffff7f]|r" |
IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] n'est pas une option valide pour |cffffff7f%s|r" |
IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] n'est pas une valeur valide pour |cffffff7f%s|r" |
NO_OPTIONS_AVAILABLE = "Pas d'options disponibles" |
OPTION_HANDLER_NOT_FOUND = "Le gestionnaire d'option |cffffff7f%q|r n'a pas \195\169t\195\169 trouv\195\169." |
OPTION_HANDLER_NOT_VALID = "Le gestionnaire d'option n'est pas valide." |
OPTION_IS_DISABLED = "L'option |cffffff7f%s|r est d\195\169sactiv\195\169e." |
KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>" -- fix |
DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?" -- fix |
elseif GetLocale() == "koKR" then |
MAP_ONOFF = { [false] = "|cffff0000ë|r", [true] = "|cff00ff00켬|r" } |
USAGE = "ì¬ì©ë²" |
IS_CURRENTLY_SET_TO = "|cffffff7f%s|r|1ì;ë; íì¬ ìíë |cffffff7f[|r%s|cffffff7f]|r|1ì¼ë¡;ë¡; ì¤ì ëì´ ììµëë¤." |
IS_NOW_SET_TO = "|cffffff7f%s|r|1ì;를; |cffffff7f[|r%s|cffffff7f]|r ìíë¡ ë³ê²½í©ëë¤." |
IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r]|1ì;ë; |cffffff7f%s|rìì ì¬ì© ë¶ê°ë¥í ì¤ì ì ëë¤." |
IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r]|1ì;ë; |cffffff7f%s|rìì ì¬ì© ë¶ê°ë¥í ì¤ì ê°ì ëë¤." |
NO_OPTIONS_AVAILABLE = "ê°ë¥í ì¤ì ì´ ììµëë¤." |
OPTION_HANDLER_NOT_FOUND = "ì¤ì ì¡°ì ê°ì¸ |cffffff7f%q|r|1ì;를; ì°¾ì§ ëª»íìµëë¤." |
OPTION_HANDLER_NOT_VALID = "ì¤ì ì¡°ì ê°ì´ ì¬ë°ë¥´ì§ ììµëë¤." |
OPTION_IS_DISABLED = "|cffffff7f%s|r ì¤ì ì ì¬ì©í ì ììµëë¤." |
KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>" |
DEFAULT_CONFIRM_MESSAGE = "ì ë§ ë¹ì ì `%s'|1ì;를; íìê² ìµëê¹?" |
elseif GetLocale() == "zhCN" then |
MAP_ONOFF = { [false] = "|cffff0000\229\133\179\233\151\173|r", [true] = "|cff00ff00\229\188\128\229\144\175|r" } |
USAGE = "\231\148\168\230\179\149" |
IS_CURRENTLY_SET_TO = "|cffffff7f%s|r \229\189\147\229\137\141\232\162\171\232\174\190\231\189\174 |cffffff7f[|r%s|cffffff7f]|r" |
IS_NOW_SET_TO = "|cffffff7f%s|r \231\142\176\229\156\168\232\162\171\232\174\190\231\189\174\228\184\186 |cffffff7f[|r%s|cffffff7f]|r" |
IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] \228\184\141\230\152\175\228\184\128\228\184\170\230\156\137\230\149\136\231\154\132\233\128\137\233\161\185 \228\184\186 |cffffff7f%s|r" |
IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] \228\184\141\230\152\175\228\184\128\228\184\170\230\156\137\230\149\136\229\128\188 \228\184\186 |cffffff7f%s|r" |
NO_OPTIONS_AVAILABLE = "\230\178\161\230\156\137\233\128\137\233\161\185\229\143\175\231\148\168" |
OPTION_HANDLER_NOT_FOUND = "\233\128\137\233\161\185\229\164\132\231\144\134\231\168\139\229\186\143 |cffffff7f%q|r \230\178\161\230\159\165\230\137\190." |
OPTION_HANDLER_NOT_VALID = "\233\128\137\233\161\185\229\164\132\231\144\134\231\168\139\229\186\143 \230\151\160\230\149\136." |
OPTION_IS_DISABLED = "\233\128\137\233\161\185 |cffffff7f%s|r \228\184\141\229\174\140\230\149\180." |
KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>" -- fix |
DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?" -- fix |
elseif GetLocale() == "zhTW" then |
MAP_ONOFF = { [false] = "|cffff0000éé|r", [true] = "|cff00ff00éå|r" } |
USAGE = "ç¨æ³" |
IS_CURRENTLY_SET_TO = "|cffffff7f%s|rç®åçè¨å®çº|cffffff7f[|r%s|cffffff7f]|r" |
IS_NOW_SET_TO = "|cffffff7f%s|rç¾å¨è¢«è¨å®çº|cffffff7f[|r%s|cffffff7f]|r" |
IS_NOT_A_VALID_OPTION_FOR = "å°æ¼|cffffff7f%2$s|rï¼[|cffffff7f%1$s|r]æ¯ä¸åä¸ç¬¦åè¦å®çé¸é " |
IS_NOT_A_VALID_VALUE_FOR = "å°æ¼|cffffff7f%2$s|rï¼[|cffffff7f%1$s|r]æ¯ä¸åä¸ç¬¦åè¦å®çæ¸å¼" |
NO_OPTIONS_AVAILABLE = "æ²æå¯ç¨çé¸é " |
OPTION_HANDLER_NOT_FOUND = "æ¾ä¸å°|cffffff7f%q|ré¸é èçå¨ã" |
OPTION_HANDLER_NOT_VALID = "é¸é èçå¨ä¸ç¬¦åè¦å®ã" |
OPTION_IS_DISABLED = "|cffffff7f%s|r已被åç¨ã" |
KEYBINDING_USAGE = "<Alt-Ctrl-Shift-éµ>" |
DEFAULT_CONFIRM_MESSAGE = "æ¯å¦å·è¡ã%sã?" |
elseif GetLocale() == "esES" then |
MAP_ONOFF = { [false] = "|cffff0000Desactivado|r", [true] = "|cff00ff00Activado|r" } |
USAGE = "Uso" |
IS_CURRENTLY_SET_TO = "|cffffff7f%s|r est\195\161 establecido actualmente a |cffffff7f[|r%s|cffffff7f]|r" |
IS_NOW_SET_TO = "|cffffff7f%s|r se ha establecido a |cffffff7f[|r%s|cffffff7f]|r" |
IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] no es una opci\195\179n valida para |cffffff7f%s|r" |
IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] no es un valor v\195\161lido para |cffffff7f%s|r" |
NO_OPTIONS_AVAILABLE = "No hay opciones disponibles" |
OPTION_HANDLER_NOT_FOUND = "Gestor de opciones |cffffff7f%q|r no encontrado." |
OPTION_HANDLER_NOT_VALID = "Gestor de opciones no v\195\161lido." |
OPTION_IS_DISABLED = "La opci\195\179n |cffffff7f%s|r est\195\161 desactivada." |
KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>" |
DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?" -- fix |
else -- enUS |
MAP_ONOFF = { [false] = "|cffff0000Off|r", [true] = "|cff00ff00On|r" } |
USAGE = "Usage" |
IS_CURRENTLY_SET_TO = "|cffffff7f%s|r is currently set to |cffffff7f[|r%s|cffffff7f]|r" |
IS_NOW_SET_TO = "|cffffff7f%s|r is now set to |cffffff7f[|r%s|cffffff7f]|r" |
IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] is not a valid option for |cffffff7f%s|r" |
IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] is not a valid value for |cffffff7f%s|r" |
NO_OPTIONS_AVAILABLE = "No options available" |
OPTION_HANDLER_NOT_FOUND = "Option handler |cffffff7f%q|r not found." |
OPTION_HANDLER_NOT_VALID = "Option handler not valid." |
OPTION_IS_DISABLED = "Option |cffffff7f%s|r is disabled." |
KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>" |
DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?" |
end |
local NONE = NONE or "None" |
local AceOO = AceLibrary("AceOO-2.0") |
local AceEvent |
local AceConsole = AceOO.Mixin { "Print", "PrintComma", "PrintLiteral", "CustomPrint", "RegisterChatCommand" } |
local Dewdrop |
local _G = getfenv(0) |
local function print(text, name, r, g, b, frame, delay) |
if not text or text:len() == 0 then |
text = " " |
end |
if not name or name == AceConsole then |
else |
text = "|cffffff78" .. tostring(name) .. ":|r " .. text |
end |
local last_color |
for t in text:gmatch("[^\n]+") do |
(frame or DEFAULT_CHAT_FRAME):AddMessage(last_color and "|cff" .. last_color .. t or t, r, g, b, nil, delay or 5) |
if not last_color or t:find("|r") or t:find("|c") then |
last_color = t:match(".*|c[fF][fF](%x%x%x%x%x%x)[^|]-$") |
end |
end |
return text |
end |
local real_tostring = tostring |
local function tostring(t) |
if type(t) == "table" then |
if type(rawget(t, 0)) == "userdata" and type(t.GetObjectType) == "function" then |
return ("<%s:%s>"):format(t:GetObjectType(), t:GetName() or "(anon)") |
end |
end |
return real_tostring(t) |
end |
local getkeystring |
local function isList(t) |
local n = #t |
for k,v in pairs(t) do |
if type(k) ~= "number" then |
return false |
elseif k < 1 or k > n then |
return false |
end |
end |
return true |
end |
local findGlobal = setmetatable({}, {__index=function(self, t) |
for k,v in pairs(_G) do |
if v == t then |
k = tostring(k) |
self[v] = k |
return k |
end |
end |
self[t] = false |
return false |
end}) |
local recurse = {} |
local timeToEnd |
local GetTime = GetTime |
local type = type |
local new, del |
do |
local cache = setmetatable({},{__mode='k'}) |
function new() |
local t = next(cache) |
if t then |
cache[t] = nil |
return t |
else |
return {} |
end |
end |
function del(t) |
for k in pairs(t) do |
t[k] = nil |
end |
cache[t] = true |
return nil |
end |
end |
local function ignoreCaseSort(alpha, bravo) |
if not alpha or not bravo then |
return false |
end |
return tostring(alpha):lower() < tostring(bravo):lower() |
end |
local function specialSort(alpha, bravo) |
if alpha == nil or bravo == nil then |
return false |
end |
local type_alpha, type_bravo = type(alpha), type(bravo) |
if type_alpha ~= type_bravo then |
return type_alpha < type_bravo |
end |
if type_alpha == "string" then |
return alpha:lower() < bravo:lower() |
elseif type_alpha == "number" then |
return alpha < bravo |
elseif type_alpha == "table" then |
return #alpha < #bravo |
elseif type_alpha == "boolean" then |
return not alpha |
else |
return false |
end |
end |
local function escapeChar(c) |
return ("\\%03d"):format(c:byte()) |
end |
local function literal_tostring_prime(t, depth) |
if type(t) == "string" then |
return ("|cff00ff00%q|r"):format((t:gsub("|", "||"))):gsub("[\001-\012\014-\031\128-\255]", escapeChar) |
elseif type(t) == "table" then |
if t == _G then |
return "|cffffea00_G|r" |
end |
if type(rawget(t, 0)) == "userdata" and type(t.GetObjectType) == "function" then |
return ("|cffffea00<%s:%s>|r"):format(t:GetObjectType(), t:GetName() or "(anon)") |
end |
if next(t) == nil then |
local mt = getmetatable(t) |
if type(mt) == "table" and type(mt.__raw) == "table" then |
t = mt.__raw |
end |
end |
if recurse[t] then |
local g = findGlobal[t] |
if g then |
return ("|cff9f9f9f<Recursion _G[%q]>|r"):format(g) |
else |
return ("|cff9f9f9f<Recursion %s>|r"):format(real_tostring(t):gsub("|", "||")) |
end |
elseif GetTime() > timeToEnd then |
local g = findGlobal[t] |
if g then |
return ("|cff9f9f9f<Timeout _G[%q]>|r"):format(g) |
else |
return ("|cff9f9f9f<Timeout %s>|r"):format(real_tostring(t):gsub("|", "||")) |
end |
elseif depth >= 2 then |
local g = findGlobal[t] |
if g then |
return ("|cff9f9f9f<_G[%q]>|r"):format(g) |
else |
return ("|cff9f9f9f<%s>|r"):format(real_tostring(t):gsub("|", "||")) |
end |
end |
recurse[t] = true |
if next(t) == nil then |
return "{}" |
elseif next(t, (next(t))) == nil then |
local k, v = next(t) |
if k == 1 then |
return "{ " .. literal_tostring_prime(v, depth+1) .. " }" |
else |
return "{ " .. getkeystring(k, depth+1) .. " = " .. literal_tostring_prime(v, depth+1) .. " }" |
end |
end |
local s |
local g = findGlobal[t] |
if g then |
s = ("{ |cff9f9f9f-- _G[%q]|r\n"):format(g) |
else |
s = "{ |cff9f9f9f-- " .. real_tostring(t):gsub("|", "||") .. "|r\n" |
end |
if isList(t) then |
for i = 1, #t do |
s = s .. (" "):rep(depth+1) .. literal_tostring_prime(t[i], depth+1) .. (i == #t and "\n" or ",\n") |
end |
else |
local tmp = new() |
for k in pairs(t) do |
tmp[#tmp+1] = k |
end |
table.sort(tmp, specialSort) |
for i,k in ipairs(tmp) do |
tmp[i] = nil |
local v = t[k] |
s = s .. (" "):rep(depth+1) .. getkeystring(k, depth+1) .. " = " .. literal_tostring_prime(v, depth+1) .. (tmp[i+1] == nil and "\n" or ",\n") |
end |
tmp = del(tmp) |
end |
if g then |
s = s .. (" "):rep(depth) .. string.format("} |cff9f9f9f-- _G[%q]|r", g) |
else |
s = s .. (" "):rep(depth) .. "} |cff9f9f9f-- " .. real_tostring(t):gsub("|", "||") |
end |
return s |
end |
if type(t) == "number" then |
return "|cffff7fff" .. real_tostring(t) .. "|r" |
elseif type(t) == "boolean" then |
return "|cffff9100" .. real_tostring(t) .. "|r" |
elseif t == nil then |
return "|cffff7f7f" .. real_tostring(t) .. "|r" |
else |
return "|cffffea00" .. real_tostring(t) .. "|r" |
end |
end |
function getkeystring(t, depth) |
if type(t) == "string" then |
if t:find("^[%a_][%a%d_]*$") then |
return "|cff7fd5ff" .. t .. "|r" |
end |
end |
return "[" .. literal_tostring_prime(t, depth) .. "]" |
end |
local get_stringed_args |
do |
local function g(value, ...) |
if select('#', ...) == 0 then |
return literal_tostring_prime(value, 1) |
end |
return literal_tostring_prime(value, 1) .. ", " .. g(...) |
end |
local function f(success, ...) |
if not success then |
return |
end |
return g(...) |
end |
function get_stringed_args(func, ...) |
return f(pcall(func, ...)) |
end |
end |
local function literal_tostring_frame(t) |
local s = ("|cffffea00<%s:%s|r\n"):format(t:GetObjectType(), t:GetName() or "(anon)") |
local __index = getmetatable(t).__index |
local tmp, tmp2, tmp3 = new(), new(), new() |
for k in pairs(t) do |
if k ~= 0 then |
tmp3[k] = true |
tmp2[k] = true |
end |
end |
for k in pairs(__index) do |
tmp2[k] = true |
end |
for k in pairs(tmp2) do |
tmp[#tmp+1] = k |
tmp2[k] = nil |
end |
table.sort(tmp, ignoreCaseSort) |
local first = true |
for i,k in ipairs(tmp) do |
local v = t[k] |
local good = true |
if k == "GetPoint" then |
for i = 1, t:GetNumPoints() do |
if not first then |
s = s .. ",\n" |
else |
first = false |
end |
s = s .. " " .. getkeystring(k, 1) .. "(" .. literal_tostring_prime(i, 1) .. ") => " .. get_stringed_args(v, t, i) |
end |
elseif type(v) == "function" and type(k) == "string" and (k:find("^Is") or k:find("^Get") or k:find("^Can")) then |
local q = get_stringed_args(v, t) |
if q then |
if not first then |
s = s .. ",\n" |
else |
first = false |
end |
s = s .. " " .. getkeystring(k, 1) .. "() => " .. q |
end |
elseif type(v) ~= "function" or (type(v) == "function" and type(k) == "string" and tmp3[k]) then |
if not first then |
s = s .. ",\n" |
else |
first = false |
end |
s = s .. " " .. getkeystring(k, 1) .. " = " .. literal_tostring_prime(v, 1) |
else |
good = false |
end |
end |
tmp, tmp2, tmp3 = del(tmp), del(tmp2), del(tmp3) |
s = s .. "\n|cffffea00>|r" |
return s |
end |
local function literal_tostring(t, only) |
timeToEnd = GetTime() + 0.2 |
local s |
if only and type(t) == "table" and type(rawget(t, 0)) == "userdata" and type(t.GetObjectType) == "function" then |
s = literal_tostring_frame(t) |
else |
s = literal_tostring_prime(t, 0) |
end |
for k,v in pairs(recurse) do |
recurse[k] = nil |
end |
for k,v in pairs(findGlobal) do |
findGlobal[k] = nil |
end |
return s |
end |
local function tostring_args(a1, ...) |
if select('#', ...) < 1 then |
return tostring(a1) |
end |
return tostring(a1), tostring_args(...) |
end |
local function literal_tostring_args(a1, ...) |
if select('#', ...) < 1 then |
return literal_tostring(a1) |
end |
return literal_tostring(a1), literal_tostring_args(...) |
end |
function AceConsole:CustomPrint(r, g, b, frame, delay, connector, a1, ...) |
if connector == true then |
local s |
if select('#', ...) == 0 then |
s = literal_tostring(a1, true) |
else |
s = (", "):join(literal_tostring_args(a1, ...)) |
end |
return print(s, self, r, g, b, frame or self.printFrame, delay) |
elseif tostring(a1):find("%%") and select('#', ...) >= 1 then |
local success, text = pcall(string.format, tostring_args(a1, ...)) |
if success then |
return print(text, self, r, g, b, frame or self.printFrame, delay) |
end |
end |
return print((connector or " "):join(tostring_args(a1, ...)), self, r, g, b, frame or self.printFrame, delay) |
end |
function AceConsole:Print(...) |
return AceConsole.CustomPrint(self, nil, nil, nil, nil, nil, " ", ...) |
end |
function AceConsole:PrintComma(...) |
return AceConsole.CustomPrint(self, nil, nil, nil, nil, nil, ", ", ...) |
end |
function AceConsole:PrintLiteral(...) |
return AceConsole.CustomPrint(self, nil, nil, nil, nil, nil, true, ...) |
end |
local work |
local argwork |
local function findTableLevel(self, options, chat, text, index, passTable) |
if not index then |
index = 1 |
if work then |
for k,v in pairs(work) do |
work[k] = nil |
end |
for k,v in pairs(argwork) do |
argwork[k] = nil |
end |
else |
work = {} |
argwork = {} |
end |
local len = text:len() |
local count |
repeat |
text, count = text:gsub("(|cff%x%x%x%x%x%x|Hitem:%d-:%d-:%d-:%d-|h%[[^%]]-) (.-%]|h|r)", "%1\001%2") |
until count == 0 |
text = text:gsub("(%]|h|r)(|cff%x%x%x%x%x%x|Hitem:%d-:%d-:%d-:%d-|h%[)", "%1 %2") |
for token in text:gmatch("([^%s]+)") do |
local token = token |
local num = tonumber(token) |
if num then |
token = num |
else |
token = token:gsub("\001", " ") |
end |
table.insert(work, token) |
end |
end |
local path = chat |
for i = 1, index - 1 do |
path = path .. " " .. tostring(work[i]) |
end |
local passValue = options.passValue or (passTable and work[index-1]) |
passTable = passTable or options |
if type(options.args) == "table" then |
local disabled, hidden = options.disabled, options.cmdHidden or options.hidden |
if hidden then |
if type(hidden) == "function" then |
hidden = hidden(passValue) |
elseif type(hidden) == "string" then |
local handler = options.handler or self |
local f = hidden |
local neg = f:match("^~(.-)$") |
if neg then |
f = neg |
end |
if type(handler[f]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f))) |
end |
hidden = handler[f](handler, passValue) |
if neg then |
hidden = not hidden |
end |
end |
end |
if hidden then |
disabled = true |
elseif disabled then |
if type(disabled) == "function" then |
disabled = disabled(passValue) |
elseif type(disabled) == "string" then |
local handler = options.handler or self |
local f = disabled |
local neg = f:match("^~(.-)$") |
if neg then |
f = neg |
end |
if type(handler[f]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f))) |
end |
disabled = handler[f](handler, passValue) |
if neg then |
disabled = not disabled |
end |
end |
end |
if not disabled then |
local next = work[index] and tostring(work[index]):lower() |
local next_num = tonumber(next) |
if next then |
for k,v in pairs(options.args) do |
local good = false |
if tostring(k):gsub("%s", "-"):lower() == next then |
good = true |
elseif k == next_num then |
good = true |
elseif type(v.aliases) == "table" then |
for _,alias in ipairs(v.aliases) do |
if alias:gsub("%s", "-"):lower() == next then |
good = true |
break |
end |
end |
elseif type(v.aliases) == "string" and v.aliases:gsub("%s", "-"):lower() == next then |
good = true |
end |
if good then |
work[index] = k -- revert it back to its original form as supplied in args |
if options.pass then |
passTable = passTable or options |
if options.get and options.set then |
passTable = options |
end |
else |
passTable = nil |
end |
return findTableLevel(options.handler or self, v, chat, text, index + 1, passTable) |
end |
end |
end |
end |
end |
for i = index, #work do |
table.insert(argwork, work[i]) |
end |
return options, path, argwork, options.handler or self, passTable, passValue |
end |
local function validateOptionsMethods(self, options, position) |
if type(options) ~= "table" then |
return "Options must be a table.", position |
end |
self = options.handler or self |
if options.type == "execute" then |
if options.func and type(options.func) ~= "string" and type(options.func) ~= "function" then |
return "func must be a string or function", position |
end |
if options.func and type(options.func) == "string" and type(self[options.func]) ~= "function" then |
return ("%q is not a proper function"):format(tostring(options.func)), position |
end |
else |
if options.get then |
if type(options.get) ~= "string" and type(options.get) ~= "function" then |
return "get must be a string or function", position |
end |
if type(options.get) == "string" then |
local f = options.get |
if options.type == "toggle" then |
f = f:match("^~(.-)$") or f |
end |
if type(self[f]) ~= "function" then |
return ("%q is not a proper function"):format(tostring(f)), position |
end |
end |
end |
if options.set then |
if type(options.set) ~= "string" and type(options.set) ~= "function" then |
return "set must be a string or function", position |
end |
if type(options.set) == "string" and type(self[options.set]) ~= "function" then |
return ("%q is not a proper function"):format(tostring(options.set)), position |
end |
end |
if options.validate and type(options.validate) ~= "table" and options.validate ~= "keybinding" then |
if type(options.validate) ~= "string" and type(options.validate) ~= "function" then |
return "validate must be a string or function", position |
end |
if type(options.validate) == "string" and type(self[options.validate]) ~= "function" then |
return ("%q is not a proper function"):format(tostring(options.validate)), position |
end |
end |
end |
if options.disabled and type(options.disabled) == "string" then |
local f = options.disabled |
f = f:match("^~(.-)$") or f |
if type(self[f]) ~= "function" then |
return ("%q is not a proper function"):format(tostring(f)), position |
end |
end |
if options.cmdHidden and type(options.cmdHidden) == "string" then |
local f = options.cmdHidden |
f = f:match("^~(.-)$") or f |
if type(self[f]) ~= "function" then |
return ("%q is not a proper function"):format(tostring(f)), position |
end |
end |
if options.guiHidden and type(options.guiHidden) == "string" then |
local f = options.guiHidden |
f = f:match("^~(.-)$") or f |
if type(self[f]) ~= "function" then |
return ("%q is not a proper function"):format(tostring(f)), position |
end |
end |
if options.hidden and type(options.hidden) == "string" then |
local f = options.hidden |
f = f:match("^~(.-)$") or f |
if type(self[f]) ~= "function" then |
return ("%q is not a proper function"):format(tostring(f)), position |
end |
end |
if options.type == "group" and type(options.args) == "table" then |
for k,v in pairs(options.args) do |
if type(v) == "table" then |
local newposition |
if position then |
newposition = position .. ".args." .. k |
else |
newposition = "args." .. k |
end |
local err, pos = validateOptionsMethods(self, v, newposition) |
if err then |
return err, pos |
end |
end |
end |
end |
end |
local function validateOptions(options, position, baseOptions, fromPass) |
if not baseOptions then |
baseOptions = options |
end |
if type(options) ~= "table" then |
return "Options must be a table.", position |
end |
local kind = options.type |
if type(kind) ~= "string" then |
return '"type" must be a string.', position |
elseif kind ~= "group" and kind ~= "range" and kind ~= "text" and kind ~= "execute" and kind ~= "toggle" and kind ~= "color" and kind ~= "header" then |
return '"type" must either be "range", "text", "group", "toggle", "execute", "color", or "header".', position |
end |
if options.aliases then |
if type(options.aliases) ~= "table" and type(options.aliases) ~= "string" then |
return '"alias" must be a table or string', position |
end |
end |
if not fromPass then |
if kind == "execute" then |
if type(options.func) ~= "string" and type(options.func) ~= "function" then |
return '"func" must be a string or function', position |
end |
elseif kind == "range" or kind == "text" or kind == "toggle" then |
if type(options.set) ~= "string" and type(options.set) ~= "function" then |
return '"set" must be a string or function', position |
end |
if kind == "text" and options.get == false then |
elseif type(options.get) ~= "string" and type(options.get) ~= "function" then |
return '"get" must be a string or function', position |
end |
elseif kind == "group" and options.pass then |
if options.pass ~= true then |
return '"pass" must be either nil, true, or false', position |
end |
if not options.func then |
if type(options.set) ~= "string" and type(options.set) ~= "function" then |
return '"set" must be a string or function', position |
end |
if type(options.get) ~= "string" and type(options.get) ~= "function" then |
return '"get" must be a string or function', position |
end |
elseif type(options.func) ~= "string" and type(options.func) ~= "function" then |
return '"func" must be a string or function', position |
end |
end |
end |
if options ~= baseOptions then |
if kind == "header" then |
elseif type(options.desc) ~= "string" then |
return '"desc" must be a string', position |
elseif options.desc:len() == 0 then |
return '"desc" cannot be a 0-length string', position |
end |
end |
if options ~= baseOptions or kind == "range" or kind == "text" or kind == "toggle" or kind == "color" then |
if options.type == "header" and not options.cmdName and not options.name then |
elseif options.cmdName then |
if type(options.cmdName) ~= "string" then |
return '"cmdName" must be a string or nil', position |
elseif options.cmdName:len() == 0 then |
return '"cmdName" cannot be a 0-length string', position |
end |
if type(options.guiName) ~= "string" then |
if not options.guiNameIsMap then |
return '"guiName" must be a string or nil', position |
end |
elseif options.guiName:len() == 0 then |
return '"guiName" cannot be a 0-length string', position |
end |
else |
if type(options.name) ~= "string" then |
return '"name" must be a string', position |
elseif options.name:len() == 0 then |
return '"name" cannot be a 0-length string', position |
end |
end |
end |
if options.guiNameIsMap then |
if type(options.guiNameIsMap) ~= "boolean" then |
return '"guiNameIsMap" must be a boolean or nil', position |
elseif options.type ~= "toggle" then |
return 'if "guiNameIsMap" is true, then "type" must be set to \'toggle\'', position |
elseif type(options.map) ~= "table" then |
return '"map" must be a table', position |
end |
end |
if options.message and type(options.message) ~= "string" then |
return '"message" must be a string or nil', position |
end |
if options.error and type(options.error) ~= "string" then |
return '"error" must be a string or nil', position |
end |
if options.current and type(options.current) ~= "string" then |
return '"current" must be a string or nil', position |
end |
if options.order then |
if type(options.order) ~= "number" or (-1 < options.order and options.order < 0.999) then |
return '"order" must be a non-zero number or nil', position |
end |
end |
if options.disabled then |
if type(options.disabled) ~= "function" and type(options.disabled) ~= "string" and options.disabled ~= true then |
return '"disabled" must be a function, string, or boolean', position |
end |
end |
if options.cmdHidden then |
if type(options.cmdHidden) ~= "function" and type(options.cmdHidden) ~= "string" and options.cmdHidden ~= true then |
return '"cmdHidden" must be a function, string, or boolean', position |
end |
end |
if options.guiHidden then |
if type(options.guiHidden) ~= "function" and type(options.guiHidden) ~= "string" and options.guiHidden ~= true then |
return '"guiHidden" must be a function, string, or boolean', position |
end |
end |
if options.hidden then |
if type(options.hidden) ~= "function" and type(options.hidden) ~= "string" and options.hidden ~= true then |
return '"hidden" must be a function, string, or boolean', position |
end |
end |
if kind == "text" then |
if type(options.validate) == "table" then |
local t = options.validate |
local iTable = nil |
for k,v in pairs(t) do |
if type(k) == "number" then |
if iTable == nil then |
iTable = true |
elseif not iTable then |
return '"validate" must either have all keys be indexed numbers or strings', position |
elseif k < 1 or k > #t then |
return '"validate" numeric keys must be indexed properly. >= 1 and <= #validate', position |
end |
else |
if iTable == nil then |
iTable = false |
elseif iTable then |
return '"validate" must either have all keys be indexed numbers or strings', position |
end |
end |
if type(v) ~= "string" then |
return '"validate" values must all be strings', position |
end |
end |
if options.multiToggle and options.multiToggle ~= true then |
return '"multiToggle" must be a boolean or nil if "validate" is a table', position |
end |
elseif options.validate == "keybinding" then |
else |
if type(options.usage) ~= "string" then |
return '"usage" must be a string', position |
elseif options.validate and type(options.validate) ~= "string" and type(options.validate) ~= "function" then |
return '"validate" must be a string, function, or table', position |
end |
end |
if options.multiToggle and type(options.validate) ~= "table" then |
return '"validate" must be a table if "multiToggle" is true', position |
end |
elseif kind == "range" then |
if options.min or options.max then |
if type(options.min) ~= "number" then |
return '"min" must be a number', position |
elseif type(options.max) ~= "number" then |
return '"max" must be a number', position |
elseif options.min >= options.max then |
return '"min" must be less than "max"', position |
end |
end |
if options.step then |
if type(options.step) ~= "number" then |
return '"step" must be a number', position |
elseif options.step < 0 then |
return '"step" must be nonnegative', position |
end |
end |
if options.isPercent and options.isPercent ~= true then |
return '"isPercent" must either be nil, true, or false', position |
end |
elseif kind == "toggle" then |
if options.map then |
if type(options.map) ~= "table" then |
return '"map" must be a table', position |
elseif type(options.map[true]) ~= "string" then |
return '"map[true]" must be a string', position |
elseif type(options.map[false]) ~= "string" then |
return '"map[false]" must be a string', position |
end |
end |
elseif kind == "color" then |
if options.hasAlpha and options.hasAlpha ~= true then |
return '"hasAlpha" must be nil, true, or false', position |
end |
elseif kind == "group" then |
if options.pass and options.pass ~= true then |
return '"pass" must be nil, true, or false', position |
end |
if type(options.args) ~= "table" then |
return '"args" must be a table', position |
end |
for k,v in pairs(options.args) do |
if type(k) ~= "number" then |
if type(k) ~= "string" then |
return '"args" keys must be strings or numbers', position |
elseif k:len() == 0 then |
return '"args" keys must not be 0-length strings.', position |
end |
end |
if type(v) ~= "table" then |
if type(k) == "number" then |
return '"args" values must be tables', position and position .. "[" .. k .. "]" or "[" .. k .. "]" |
else |
return '"args" values must be tables', position and position .. "." .. k or k |
end |
end |
local newposition |
if type(k) == "number" then |
newposition = position and position .. ".args[" .. k .. "]" or "args[" .. k .. "]" |
else |
newposition = position and position .. ".args." .. k or "args." .. k |
end |
local err, pos = validateOptions(v, newposition, baseOptions, options.pass) |
if err then |
return err, pos |
end |
end |
elseif kind == "execute" then |
if type(options.confirm) ~= "string" and type(options.confirm) ~= "boolean" and type(options.confirm) ~= "nil" then |
return '"confirm" must be a string, boolean, or nil', position |
end |
end |
end |
local colorTable |
local colorFunc |
local colorCancelFunc |
local function keybindingValidateFunc(text) |
if text == nil or text == "NONE" then |
return nil |
end |
text = text:upper() |
local shift, ctrl, alt |
local modifier |
while true do |
if text == "-" then |
break |
end |
modifier, text = strsplit('-', text, 2) |
if text then |
if modifier ~= "SHIFT" and modifier ~= "CTRL" and modifier ~= "ALT" then |
return false |
end |
if modifier == "SHIFT" then |
if shift then |
return false |
end |
shift = true |
end |
if modifier == "CTRL" then |
if ctrl then |
return false |
end |
ctrl = true |
end |
if modifier == "ALT" then |
if alt then |
return false |
end |
alt = true |
end |
else |
text = modifier |
break |
end |
end |
if not text:find("^F%d+$") and text ~= "CAPSLOCK" and text:len() ~= 1 and (text:byte() < 128 or text:len() > 4) and not _G["KEY_" .. text] then |
return false |
end |
local s = text |
if shift then |
s = "SHIFT-" .. s |
end |
if ctrl then |
s = "CTRL-" .. s |
end |
if alt then |
s = "ALT-" .. s |
end |
return s |
end |
AceConsole.keybindingValidateFunc = keybindingValidateFunc |
local order |
local mysort_args |
local mysort |
local function icaseSort(alpha, bravo) |
if type(alpha) == "number" and type(bravo) == "number" then |
return alpha < bravo |
end |
return tostring(alpha):lower() < tostring(bravo):lower() |
end |
local tmp = {} |
local function printUsage(self, handler, realOptions, options, path, args, passValue, quiet, filter) |
if filter then |
filter = "^" .. filter:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") |
end |
local hidden, disabled = options.cmdHidden or options.hidden, options.disabled |
if hidden then |
if type(hidden) == "function" then |
hidden = hidden(options.passValue) |
elseif type(hidden) == "string" then |
local f = hidden |
local neg = f:match("^~(.-)$") |
if neg then |
f = neg |
end |
if type(handler[f]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f))) |
end |
hidden = handler[f](handler, options.passValue) |
if neg then |
hidden = not hidden |
end |
end |
end |
if hidden then |
disabled = true |
elseif disabled then |
if type(disabled) == "function" then |
disabled = disabled(options.passValue) |
elseif type(disabled) == "string" then |
local f = disabled |
local neg = f:match("^~(.-)$") |
if neg then |
f = neg |
end |
if type(handler[f]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f))) |
end |
disabled = handler[f](handler, options.passValue) |
if neg then |
disabled = not disabled |
end |
end |
end |
local kind = (options.type or "group"):lower() |
if disabled then |
print(OPTION_IS_DISABLED:format(path), realOptions.cmdName or realOptions.name or self) |
elseif kind == "text" then |
local var |
local multiToggle |
for k in pairs(tmp) do |
tmp[k] = nil |
end |
if passTable then |
multiToggle = passTable.multiToggle |
if not passTable.get then |
elseif type(passTable.get) == "function" then |
if not multiToggle then |
var = passTable.get(passValue) |
else |
var = tmp |
for k,v in pairs(options.validate) do |
local val = type(k) ~= "number" and k or v |
if passValue == nil then |
var[val] = passTable.get(val) or nil |
else |
var[val] = passTable.get(passValue, val) or nil |
end |
end |
end |
else |
local handler = passTable.handler or handler |
if type(handler[passTable.get]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(passTable.get))) |
end |
var = handler[passTable.get](handler, passValue) |
if not multiToggle then |
var = handler[passTable.get](handler, passValue) |
else |
var = tmp |
for k,v in pairs(options.validate) do |
local val = type(k) ~= "number" and k or v |
if passValue == nil then |
var[val] = handler[passTable.get](handler, val) or nil |
else |
var[val] = handler[passTable.get](handler, passValue, val) or nil |
end |
end |
end |
end |
else |
multiToggle = options.multiToggle |
if not options.get then |
elseif type(options.get) == "function" then |
if not multiToggle then |
var = options.get(passValue) |
else |
var = tmp |
for k,v in pairs(options.validate) do |
local val = type(k) ~= "number" and k or v |
if passValue == nil then |
var[val] = options.get(val) or nil |
else |
var[val] = options.get(passValue, val) or nil |
end |
end |
end |
else |
local handler = options.handler or handler |
if type(handler[options.get]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options.get))) |
end |
if not multiToggle then |
var = handler[options.get](handler, passValue) |
else |
var = tmp |
for k,v in pairs(options.validate) do |
local val = type(k) ~= "number" and k or v |
if passValue == nil then |
var[val] = handler[options.get](handler, val) or nil |
else |
var[val] = handler[options.get](handler, passValue, val) or nil |
end |
end |
end |
end |
end |
local usage |
if type(options.validate) == "table" then |
if filter then |
if not order then |
order = {} |
end |
for k,v in pairs(options.validate) do |
if v:find(filter) then |
table.insert(order, v) |
end |
end |
table.sort(order, icaseSort) |
usage = "{" .. table.concat(order, " || ") .. "}" |
for k in pairs(order) do |
order[k] = nil |
end |
else |
if not order then |
order = {} |
end |
for k,v in pairs(options.validate) do |
table.insert(order, v) |
end |
table.sort(order, icaseSort) |
usage = "{" .. table.concat(order, " || ") .. "}" |
for k in pairs(order) do |
order[k] = nil |
end |
end |
if multiToggle then |
if not next(var) then |
var = NONE |
else |
if not order then |
order = {} |
end |
for k in pairs(var) do |
if options.validate[k] then |
order[#order+1] = options.validate[k] |
else |
for _,v in pairs(options.validate) do |
if v == k or (type(v) == "string" and type(k) == "string" and v:lower() == k:lower()) then |
order[#order+1] = v |
break |
end |
end |
end |
end |
table.sort(order, icaseSort) |
var = table.concat(order, ", ") |
for k in pairs(order) do |
order[k] = nil |
end |
end |
else |
var = options.validate[var] or var |
end |
elseif options.validate == "keybinding" then |
usage = KEYBINDING_USAGE |
else |
usage = options.usage or "<value>" |
end |
if not quiet then |
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, usage), realOptions.cmdName or realOptions.name or self) |
end |
if (passTable and passTable.get) or options.get then |
print((options.current or IS_CURRENTLY_SET_TO):format(tostring(options.cmdName or options.name), tostring(var or NONE))) |
end |
elseif kind == "range" then |
local var |
if passTable then |
if type(passTable.get) == "function" then |
var = passTable.get(passValue) |
else |
local handler = passTable.handler or handler |
if type(handler[passTable.get]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(passTable.get))) |
end |
var = handler[passTable.get](handler, passValue) |
end |
else |
if type(options.get) == "function" then |
var = options.get(passValue) |
else |
local handler = options.handler or handler |
if type(handler[options.get]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options.get))) |
end |
var = handler[options.get](handler, passValue) |
end |
end |
local usage |
local min = options.min or 0 |
local max = options.max or 1 |
if options.isPercent then |
min, max = min * 100, max * 100 |
var = tostring(var * 100) .. "%" |
end |
local bit = "-" |
if min < 0 or max < 0 then |
bit = " - " |
end |
usage = ("(%s%s%s)"):format(min, bit, max) |
if not quiet then |
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, usage), realOptions.cmdName or realOptions.name or self) |
end |
print((options.current or IS_CURRENTLY_SET_TO):format(tostring(options.cmdName or options.name), tostring(var or NONE))) |
elseif kind == "group" then |
local usage |
if next(options.args) then |
if not order then |
order = {} |
end |
for k,v in pairs(options.args) do |
if v.type ~= "header" then |
local hidden = v.cmdHidden or v.hidden |
if hidden then |
if type(hidden) == "function" then |
hidden = hidden() |
elseif type(hidden) == "string" then |
local f = hidden |
local neg = f:match("^~(.-)$") |
if neg then |
f = neg |
end |
if type(handler[f]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f))) |
end |
hidden = handler[f](handler) |
if neg then |
hidden = not hidden |
end |
end |
end |
if not hidden then |
if filter then |
if k:find(filter) then |
table.insert(order, k) |
elseif type(v.aliases) == "table" then |
for _,bit in ipairs(v.aliases) do |
if bit:find(filter) then |
table.insert(order, k) |
break |
end |
end |
elseif type(v.aliases) == "string" then |
if v.aliases:find(filter) then |
table.insert(order, k) |
end |
end |
else |
table.insert(order, k) |
end |
end |
end |
end |
if not mysort then |
mysort = function(a, b) |
local alpha, bravo = mysort_args[a], mysort_args[b] |
local alpha_order = alpha and alpha.order or 100 |
local bravo_order = bravo and bravo.order or 100 |
if alpha_order == bravo_order then |
return tostring(a):lower() < tostring(b):lower() |
else |
if alpha_order < 0 then |
if bravo_order > 0 then |
return false |
end |
else |
if bravo_order < 0 then |
return true |
end |
end |
if alpha_order > 0 and bravo_order > 0 then |
return tostring(a):lower() < tostring(b):lower() |
end |
return alpha_order < bravo_order |
end |
end |
end |
mysort_args = options.args |
table.sort(order, mysort) |
mysort_args = nil |
if not quiet then |
if options == realOptions then |
if options.desc then |
print(tostring(options.desc), realOptions.cmdName or realOptions.name or self) |
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, "{" .. table.concat(order, " || ") .. "}")) |
elseif self.description or self.notes then |
print(tostring(self.description or self.notes), realOptions.cmdName or realOptions.name or self) |
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, "{" .. table.concat(order, " || ") .. "}")) |
else |
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self) |
end |
else |
if options.desc then |
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self) |
print(tostring(options.desc)) |
else |
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self) |
end |
end |
end |
local passTable = options.pass and options or nil |
for _,k in ipairs(order) do |
local passValue = passTable and k or nil |
local real_k = k |
local v = options.args[k] |
if v then |
local v_p = passTable or v |
if v.get and v.set then |
v_p = v |
passValue = nil |
end |
if v.passValue then |
passValue = v.passValue |
end |
local k = tostring(k):gsub("%s", "-") |
local disabled = v.disabled |
if disabled then |
if type(disabled) == "function" then |
disabled = disabled(passValue) |
elseif type(disabled) == "string" then |
local f = disabled |
local neg = f:match("^~(.-)$") |
if neg then |
f = neg |
end |
if type(handler[f]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f))) |
end |
disabled = handler[f](handler, passValue) |
if neg then |
disabled = not disabled |
end |
end |
end |
if type(v.aliases) == "table" then |
for _,s in ipairs(v.aliases) do |
k = k .. " || " .. s:gsub("%s", "-") |
end |
elseif type(v.aliases) == "string" then |
k = k .. " || " .. v.aliases:gsub("%s", "-") |
end |
if v_p.get then |
local a1,a2,a3,a4 |
local multiToggle = v_p.type == "text" and v_p.multiToggle |
for k in pairs(tmp) do |
tmp[k] = nil |
end |
if type(v_p.get) == "function" then |
if multiToggle then |
a1 = tmp |
for k,v in pairs(v.validate) do |
local val = type(k) ~= "number" and k or v |
if passValue == nil then |
a1[val] = v_p.get(val) or nil |
else |
a1[val] = v_p.get(passValue, val) or nil |
end |
end |
else |
a1,a2,a3,a4 = v_p.get(passValue) |
end |
else |
local handler = v_p.handler or handler |
local f = v_p.get |
local neg |
if v.type == "toggle" then |
neg = f:match("^~(.-)$") |
if neg then |
f = neg |
end |
end |
if type(handler[f]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f))) |
end |
if multiToggle then |
a1 = tmp |
for k,v in pairs(v.validate) do |
local val = type(k) ~= "number" and k or v |
if passValue == nil then |
a1[val] = handler[f](handler, val) or nil |
else |
a1[val] = handler[f](handler, passValue, val) or nil |
end |
end |
else |
a1,a2,a3,a4 = handler[f](handler, passValue) |
end |
if neg then |
a1 = not a1 |
end |
end |
local s |
if v.type == "color" then |
if v.hasAlpha then |
if not a1 or not a2 or not a3 or not a4 then |
s = NONE |
else |
s = ("|c%02x%02x%02x%02x%02x%02x%02x%02x|r"):format(a4*255, a1*255, a2*255, a3*255, a4*255, a1*255, a2*255, a3*255) |
end |
else |
if not a1 or not a2 or not a3 then |
s = NONE |
else |
s = ("|cff%02x%02x%02x%02x%02x%02x|r"):format(a1*255, a2*255, a3*255, a1*255, a2*255, a3*255) |
end |
end |
elseif v.type == "toggle" then |
if v.map then |
s = tostring(v.map[a1 and true or false] or NONE) |
else |
s = tostring(MAP_ONOFF[a1 and true or false] or NONE) |
end |
elseif v.type == "range" then |
if v.isPercent then |
s = tostring(a1 * 100) .. "%" |
else |
s = tostring(a1) |
end |
elseif v.type == "text" and type(v.validate) == "table" then |
if multiToggle then |
if not next(a1) then |
s = NONE |
else |
s = '' |
for k in pairs(a1) do |
if v.validate[k] then |
if s == '' then |
s = v.validate[k] |
else |
s = s .. ', ' .. v.validate[k] |
end |
else |
for _,u in pairs(v.validate) do |
if u == k or (type(v) == "string" and type(k) == "string" and v:lower() == k:lower()) then |
if s == '' then |
s = u |
else |
s = s .. ', ' .. u |
end |
break |
end |
end |
end |
end |
end |
else |
s = tostring(v.validate[a1] or a1 or NONE) |
end |
else |
s = tostring(a1 or NONE) |
end |
if disabled then |
local s = s:gsub("|cff%x%x%x%x%x%x(.-)|r", "%1") |
local desc = (v.desc or NONE):gsub("|cff%x%x%x%x%x%x(.-)|r", "%1") |
print(("|cffcfcfcf - %s: [%s] %s|r"):format(k, s, desc)) |
else |
print((" - |cffffff7f%s: [|r%s|cffffff7f]|r %s"):format(k, s, v.desc or NONE)) |
end |
else |
if disabled then |
local desc = (v.desc or NONE):gsub("|cff%x%x%x%x%x%x(.-)|r", "%1") |
print(("|cffcfcfcf - %s: %s"):format(k, desc)) |
else |
print((" - |cffffff7f%s:|r %s"):format(k, v.desc or NONE)) |
end |
end |
end |
end |
for k in pairs(order) do |
order[k] = nil |
end |
else |
if options.desc then |
print(("|cffffff7f%s:|r %s"):format(USAGE, path), realOptions.cmdName or realOptions.name or self) |
print(tostring(options.desc)) |
elseif options == realOptions and (self.description or self.notes) then |
print(tostring(self.description or self.notes), realOptions.cmdName or realOptions.name or self) |
print(("|cffffff7f%s:|r %s"):format(USAGE, path)) |
else |
print(("|cffffff7f%s:|r %s"):format(USAGE, path), realOptions.cmdName or realOptions.name or self) |
end |
print(NO_OPTIONS_AVAILABLE) |
end |
end |
end |
local function confirmPopup(message, func, ...) |
if not StaticPopupDialogs["ACECONSOLE20_CONFIRM_DIALOG"] then |
StaticPopupDialogs["ACECONSOLE20_CONFIRM_DIALOG"] = {} |
end |
local t = StaticPopupDialogs["ACECONSOLE20_CONFIRM_DIALOG"] |
for k in pairs(t) do |
t[k] = nil |
end |
t.text = message |
t.button1 = ACCEPT or "Accept" |
t.button2 = CANCEL or "Cancel" |
t.OnAccept = function() |
func(unpack(t)) |
end |
for i = 1, select('#', ...) do |
t[i] = select(i, ...) |
end |
t.timeout = 0 |
t.whileDead = 1 |
t.hideOnEscape = 1 |
StaticPopup_Show("ACECONSOLE20_CONFIRM_DIALOG") |
end |
local function handlerFunc(self, chat, msg, options) |
if not msg then |
msg = "" |
else |
msg = msg:gsub("^%s*(.-)%s*$", "%1") |
msg = msg:gsub("%s+", " ") |
end |
local realOptions = options |
local options, path, args, handler, passTable, passValue = findTableLevel(self, options, chat, msg) |
if options.type == "execute" then |
if options.func then |
passTable = nil |
end |
else |
if options.get and options.set then |
passTable = nil |
end |
end |
passValue = options.passValue or passTable and passValue |
local hidden, disabled = options.cmdHidden or options.hidden, options.disabled |
if hidden then |
if type(hidden) == "function" then |
hidden = hidden(passValue) |
elseif type(hidden) == "string" then |
local f = hidden |
local neg = f:match("^~(.-)$") |
if neg then |
f = neg |
end |
if type(handler[f]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f))) |
end |
hidden = handler[f](handler, passValue) |
if neg then |
hidden = not hidden |
end |
end |
end |
if hidden then |
disabled = true |
elseif disabled then |
if type(disabled) == "function" then |
disabled = disabled(passValue) |
elseif type(disabled) == "string" then |
local f = disabled |
local neg = f:match("^~(.-)$") |
if neg then |
f = neg |
end |
if type(handler[f]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f))) |
end |
disabled = handler[f](handler, passValue) |
if neg then |
disabled = not disabled |
end |
end |
end |
local _G_this = this |
local kind = (options.type or "group"):lower() |
local options_p = passTable or options |
if disabled then |
print(OPTION_IS_DISABLED:format(path), realOptions.cmdName or realOptions.name or self) |
elseif kind == "text" then |
if #args > 0 then |
if (type(options.validate) == "table" and #args > 1) or (type(options.validate) ~= "table" and not options.input) then |
local arg = table.concat(args, " ") |
for k,v in pairs(args) do |
args[k] = nil |
end |
args[1] = arg |
end |
if options.validate then |
local good |
if type(options.validate) == "function" then |
good = options.validate(unpack(args)) |
elseif type(options.validate) == "table" then |
local arg = args[1] |
arg = tostring(arg):lower() |
for k,v in pairs(options.validate) do |
if v:lower() == arg then |
args[1] = type(k) == "string" and k or v |
good = true |
break |
end |
end |
if not good and type((next(options.validate))) == "string" then |
for k,v in pairs(options.validate) do |
if type(k) == "string" and k:lower() == arg then |
args[1] = k |
good = true |
break |
end |
end |
end |
elseif options.validate == "keybinding" then |
good = keybindingValidateFunc(unpack(args)) |
if good ~= false then |
args[1] = good |
end |
else |
if type(handler[options.validate]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options.validate))) |
end |
good = handler[options.validate](handler, unpack(args)) |
end |
if not good then |
local usage |
if type(options.validate) == "table" then |
if not order then |
order = {} |
end |
for k,v in pairs(options.validate) do |
table.insert(order, v) |
end |
usage = "{" .. table.concat(order, " || ") .. "}" |
for k in pairs(order) do |
order[k] = nil |
end |
elseif options.validate == "keybinding" then |
usage = KEYBINDING_USAGE |
else |
usage = options.usage or "<value>" |
end |
print((options.error or IS_NOT_A_VALID_OPTION_FOR):format(tostring(table.concat(args, " ")), path), realOptions.cmdName or realOptions.name or self) |
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, usage)) |
return |
end |
end |
local var |
local multiToggle |
for k in pairs(tmp) do |
tmp[k] = nil |
end |
multiToggle = options_p.multiToggle |
if not options_p.get then |
elseif type(options_p.get) == "function" then |
if multiToggle then |
var = tmp |
for k,v in pairs(options.validate) do |
local val = type(k) ~= "number" and k or v |
if passValue then |
var[val] = options_p.get(passValue, val) or nil |
else |
var[val] = options_p.get(val) or nil |
end |
end |
else |
var = options_p.get(passValue) |
end |
else |
if type(handler[options_p.get]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get))) |
end |
if multiToggle then |
var = tmp |
for k,v in pairs(options.validate) do |
local val = type(k) ~= "number" and k or v |
if passValue then |
var[val] = handler[options_p.get](handler, passValue, val) or nil |
else |
var[val] = handler[options_p.get](handler, val) or nil |
end |
end |
else |
var = handler[options_p.get](handler, passValue) |
end |
end |
if multiToggle or var ~= args[1] then |
if multiToggle then |
local current = var[args[1]] |
if current == nil and type(args[1]) == "string" then |
for k in pairs(var) do |
if type(k) == "string" and k:lower() == args[1]:lower() then |
current = true |
break |
end |
end |
end |
args[2] = not current |
end |
if type(options_p.set) == "function" then |
if passValue then |
options_p.set(passValue, unpack(args)) |
else |
options_p.set(unpack(args)) |
end |
else |
if type(handler[options_p.set]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.set))) |
end |
if passValue then |
handler[options_p.set](handler, passValue, unpack(args)) |
else |
handler[options_p.set](handler, unpack(args)) |
end |
end |
end |
end |
if #args > 0 then |
local var |
local multiToggle |
for k in pairs(tmp) do |
tmp[k] = nil |
end |
multiToggle = options_p.multiToggle |
if not options_p.get then |
elseif type(options_p.get) == "function" then |
if multiToggle then |
var = tmp |
for k,v in pairs(options_p.validate) do |
local val = type(k) ~= "number" and k or v |
if passValue then |
var[val] = options_p.get(passValue, val) or nil |
else |
var[val] = options_p.get(val) or nil |
end |
end |
else |
var = options_p.get(passValue) |
end |
else |
if type(handler[options_p.get]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get))) |
end |
if multiToggle then |
var = tmp |
for k,v in pairs(options.validate) do |
local val = type(k) ~= "number" and k or v |
if passValue then |
var[val] = handler[options_p.get](handler, passValue, val) or nil |
else |
var[val] = handler[options_p.get](handler, val) or nil |
end |
end |
else |
var = handler[options_p.get](handler, passValue) |
end |
end |
if multiToggle then |
if not next(var) then |
var = NONE |
else |
if not order then |
order = {} |
end |
for k in pairs(var) do |
if options.validate[k] then |
order[#order+1] = options.validate[k] |
else |
for _,v in pairs(options.validate) do |
if v == k or (type(v) == "string" and type(k) == "string" and v:lower() == k:lower()) then |
order[#order+1] = v |
break |
end |
end |
end |
end |
table.sort(order, icaseSort) |
var = table.concat(order, ", ") |
for k in pairs(order) do |
order[k] = nil |
end |
end |
elseif type(options.validate) == "table" then |
var = options.validate[var] or var |
end |
if options_p.get then |
print((options.message or IS_NOW_SET_TO):format(tostring(options.cmdName or options.name), tostring(var or NONE)), realOptions.cmdName or realOptions.name or self) |
end |
if var == args[1] then |
return |
end |
else |
printUsage(self, handler, realOptions, options, path, args, passValue) |
return |
end |
elseif kind == "execute" then |
local confirm = options.confirm |
if confirm == true then |
confirm = DEFAULT_CONFIRM_MESSAGE:format(options.desc or options.name or UNKNOWN or "Unknown") |
end |
if type(options_p.func) == "function" then |
if confirm then |
confirmPopup(confirm, options_p.func, passValue) |
else |
options_p.func(passValue) |
end |
else |
if type(handler[options_p.func]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.func))) |
end |
if confirm then |
confirmPopup(confirm, handler[options_p.func], handler, passValue) |
else |
handler[options_p.func](handler, passValue) |
end |
end |
elseif kind == "toggle" then |
local var |
if type(options_p.get) == "function" then |
var = options_p.get(passValue) |
else |
local f = options_p.get |
local neg = f:match("^~(.-)$") |
if neg then |
f = neg |
end |
if type(handler[f]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f))) |
end |
var = handler[f](handler, passValue) |
if neg then |
var = not var |
end |
end |
if type(options_p.set) == "function" then |
if passValue then |
options_p.set(passValue, not var) |
else |
options_p.set(not var) |
end |
else |
if type(handler[options_p.set]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.set))) |
end |
if passValue then |
handler[options_p.set](handler, passValue, not var) |
else |
handler[options_p.set](handler, not var) |
end |
end |
if type(options_p.get) == "function" then |
var = options_p.get(passValue) |
else |
local f = options_p.get |
local neg = f:match("^~(.-)$") |
if neg then |
f = neg |
end |
var = handler[f](handler, passValue) |
if neg then |
var = not var |
end |
end |
print((options.message or IS_NOW_SET_TO):format(tostring(options.cmdName or options.name), (options.map or MAP_ONOFF)[var and true or false] or NONE), realOptions.cmdName or realOptions.name or self) |
elseif kind == "range" then |
local arg |
if #args <= 1 then |
arg = args[1] |
else |
arg = table.concat(args, " ") |
end |
if arg then |
local min = options.min or 0 |
local max = options.max or 1 |
local good = false |
if type(arg) == "number" then |
if options.isPercent then |
arg = arg / 100 |
end |
if arg >= min and arg <= max then |
good = true |
end |
if good and type(options.step) == "number" and options.step > 0 then |
local step = options.step |
arg = math.floor((arg - min) / step + 0.5) * step + min |
if arg > max then |
arg = max |
elseif arg < min then |
arg = min |
end |
end |
end |
if not good then |
local usage |
local min = options.min or 0 |
local max = options.max or 1 |
if options.isPercent then |
min, max = min * 100, max * 100 |
end |
local bit = "-" |
if min < 0 or max < 0 then |
bit = " - " |
end |
usage = ("(%s%s%s)"):format(min, bit, max) |
print((options.error or IS_NOT_A_VALID_VALUE_FOR):format(tostring(arg), path), realOptions.cmdName or realOptions.name or self) |
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, usage)) |
return |
end |
local var |
if type(options_p.get) == "function" then |
var = options_p.get(passValue) |
else |
if type(handler[options_p.get]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get))) |
end |
var = handler[options_p.get](handler, passValue) |
end |
if var ~= arg then |
if type(options_p.set) == "function" then |
if passValue then |
options_p.set(passValue, arg) |
else |
options_p.set(arg) |
end |
else |
if type(handler[options_p.set]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.set))) |
end |
if passValue then |
handler[options_p.set](handler, passValue, arg) |
else |
handler[options_p.set](handler, arg) |
end |
end |
end |
end |
if arg then |
local var |
if type(options_p.get) == "function" then |
var = options_p.get(passValue) |
else |
if type(handler[options_p.get]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get))) |
end |
var = handler[options_p.get](handler, passValue) |
end |
if var and options.isPercent then |
var = tostring(var * 100) .. "%" |
end |
print((options.message or IS_NOW_SET_TO):format(tostring(options.cmdName or options.name), tostring(var or NONE)), realOptions.cmdName or realOptions.name or self) |
if var == arg then |
return |
end |
else |
printUsage(self, handler, realOptions, options, path, args, passValue) |
return |
end |
elseif kind == "color" then |
if #args > 0 then |
local r,g,b,a |
if #args == 1 then |
local arg = tostring(args[1]) |
if options.hasAlpha then |
if arg:len() == 8 and arg:find("^%x*$") then |
r,g,b,a = tonumber(arg:sub(1, 2), 16) / 255, tonumber(arg:sub(3, 4), 16) / 255, tonumber(arg:sub(5, 6), 16) / 255, tonumber(arg:sub(7, 8), 16) / 255 |
end |
else |
if arg:len() == 6 and arg:find("^%x*$") then |
r,g,b = tonumber(arg:sub(1, 2), 16) / 255, tonumber(arg:sub(3, 4), 16) / 255, tonumber(arg:sub(5, 6), 16) / 255 |
end |
end |
elseif #args == 4 and options.hasAlpha then |
local a1,a2,a3,a4 = args[1], args[2], args[3], args[4] |
if type(a1) == "number" and type(a2) == "number" and type(a3) == "number" and type(a4) == "number" and a1 <= 1 and a2 <= 1 and a3 <= 1 and a4 <= 1 then |
r,g,b,a = a1,a2,a3,a4 |
elseif (type(a1) == "number" or a1:len() == 2) and a1:find("^%x*$") and (type(a2) == "number" or a2:len() == 2) and a2:find("^%x*$") and (type(a3) == "number" or a3:len() == 2) and a3:find("^%x*$") and (type(a4) == "number" or a4:len() == 2) and a4:find("^%x*$") then |
r,g,b,a = tonumber(a1, 16) / 255, tonumber(a2, 16) / 255, tonumber(a3, 16) / 255, tonumber(a4, 16) / 255 |
end |
elseif #args == 3 and not options.hasAlpha then |
local a1,a2,a3 = args[1], args[2], args[3] |
if type(a1) == "number" and type(a2) == "number" and type(a3) == "number" and a1 <= 1 and a2 <= 1 and a3 <= 1 then |
r,g,b = a1,a2,a3 |
elseif (type(a1) == "number" or a1:len() == 2) and a1:find("^%x*$") and (type(a2) == "number" or a2:len() == 2) and a2:find("^%x*$") and (type(a3) == "number" or a3:len() == 2) and a3:find("^%x*$") then |
r,g,b = tonumber(a1, 16) / 255, tonumber(a2, 16) / 255, tonumber(a3, 16) / 255 |
end |
end |
if not r then |
print((options.error or IS_NOT_A_VALID_OPTION_FOR):format(table.concat(args, ' '), path), realOptions.cmdName or realOptions.name or self) |
print(("|cffffff7f%s:|r %s {0-1} {0-1} {0-1}%s"):format(USAGE, path, options.hasAlpha and " {0-1}" or "")) |
return |
end |
if type(options_p.set) == "function" then |
if passValue then |
options_p.set(passValue, r,g,b,a) |
else |
options_p.set(r,g,b,a) |
end |
else |
if type(handler[options_p.set]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.set))) |
end |
if passValue then |
handler[options_p.set](handler, passValue, r,g,b,a) |
else |
handler[options_p.set](handler, r,g,b,a) |
end |
end |
local r,g,b,a |
if type(options_p.get) == "function" then |
r,g,b,a = options_p.get(passValue) |
else |
if type(handler[options_p.get]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get))) |
end |
r,g,b,a = handler[options_p.get](handler, passValue) |
end |
local s |
if type(r) == "number" and type(g) == "number" and type(b) == "number" then |
if options.hasAlpha and type(a) == "number" then |
s = ("|c%02x%02x%02x%02x%02x%02x%02x%02x|r"):format(a*255, r*255, g*255, b*255, r*255, g*255, b*255, a*255) |
else |
s = ("|cff%02x%02x%02x%02x%02x%02x|r"):format(r*255, g*255, b*255, r*255, g*255, b*255) |
end |
else |
s = NONE |
end |
print((options.message or IS_NOW_SET_TO):format(tostring(options.cmdName or options.name), s), realOptions.cmdName or realOptions.name or self) |
else |
local r,g,b,a |
if type(options_p.get) == "function" then |
r,g,b,a = options_p.get(passValue) |
else |
if type(handler[options_p.get]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get))) |
end |
r,g,b,a = handler[options_p.get](handler, passValue) |
end |
if not colorTable then |
colorTable = {} |
local t = colorTable |
if ColorPickerOkayButton then |
local ColorPickerOkayButton_OnClick = ColorPickerOkayButton:GetScript("OnClick") |
ColorPickerOkayButton:SetScript("OnClick", function() |
if ColorPickerOkayButton_OnClick then |
ColorPickerOkayButton_OnClick() |
end |
if t.active then |
ColorPickerFrame.cancelFunc = nil |
ColorPickerFrame.func = nil |
ColorPickerFrame.opacityFunc = nil |
local r,g,b,a |
if t.passValue then |
if type(t.get) == "function" then |
r,g,b,a = t.get(t.passValue) |
else |
if type(t.handler[t.get]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.get))) |
end |
r,g,b,a = t.handler[t.get](t.handler, t.passValue) |
end |
else |
if type(t.get) == "function" then |
r,g,b,a = t.get() |
else |
if type(t.handler[t.get]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.get))) |
end |
r,g,b,a = t.handler[t.get](t.handler) |
end |
end |
if r ~= t.r or g ~= t.g or b ~= t.b or (t.hasAlpha and a ~= t.a) then |
local s |
if type(r) == "number" and type(g) == "number" and type(b) == "number" then |
if t.hasAlpha and type(a) == "number" then |
s = ("|c%02x%02x%02x%02x%02x%02x%02x%02x|r"):format(a*255, r*255, g*255, b*255, r*255, g*255, b*255, a*255) |
else |
s = ("|cff%02x%02x%02x%02x%02x%02x|r"):format(r*255, g*255, b*255, r*255, g*255, b*255) |
end |
else |
s = NONE |
end |
print(t.message:format(tostring(t.name), s), t.realOptions.cmdName or t.realOptions.name or self) |
end |
for k,v in pairs(t) do |
t[k] = nil |
end |
end |
end) |
end |
else |
for k,v in pairs(colorTable) do |
colorTable[k] = nil |
end |
end |
if type(r) ~= "number" or type(g) ~= "number" or type(b) ~= "number" then |
r,g,b = 1, 1, 1 |
end |
if type(a) ~= "number" then |
a = 1 |
end |
local t = colorTable |
t.r = r |
t.g = g |
t.b = b |
if hasAlpha then |
t.a = a |
end |
t.realOptions = realOptions |
t.hasAlpha = options.hasAlpha |
t.handler = handler |
t.set = options_p.set |
t.get = options_p.get |
t.name = options.cmdName or options.name |
t.message = options.message or IS_NOW_SET_TO |
t.passValue = passValue |
t.active = true |
if not colorFunc then |
colorFunc = function() |
local r,g,b = ColorPickerFrame:GetColorRGB() |
if t.hasAlpha then |
local a = 1 - OpacitySliderFrame:GetValue() |
if type(t.set) == "function" then |
if t.passValue then |
t.set(t.passValue, r,g,b,a) |
else |
t.set(r,g,b,a) |
end |
else |
if type(t.handler[t.set]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.set))) |
end |
if t.passValue then |
t.handler[t.set](t.handler, t.passValue, r,g,b,a) |
else |
t.handler[t.set](t.handler, r,g,b,a) |
end |
end |
else |
if type(t.set) == "function" then |
if t.passValue then |
t.set(t.passValue, r,g,b) |
else |
t.set(r,g,b) |
end |
else |
if type(t.handler[t.set]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.set))) |
end |
if t.passValue then |
t.handler[t.set](t.handler, t.passValue, r,g,b) |
else |
t.handler[t.set](t.handler, r,g,b) |
end |
end |
end |
end |
end |
ColorPickerFrame.func = colorFunc |
ColorPickerFrame.hasOpacity = options.hasAlpha |
if options.hasAlpha then |
ColorPickerFrame.opacityFunc = ColorPickerFrame.func |
ColorPickerFrame.opacity = 1 - a |
end |
ColorPickerFrame:SetColorRGB(r,g,b) |
if not colorCancelFunc then |
colorCancelFunc = function() |
if t.hasAlpha then |
if type(t.set) == "function" then |
if t.passValue then |
t.set(t.passValue, t.r,t.g,t.b,t.a) |
else |
t.set(t.r,t.g,t.b,t.a) |
end |
else |
if type(t.handler[t.get]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.get))) |
end |
if t.passValue then |
t.handler[t.set](t.handler, t.passValue, t.r,t.g,t.b,t.a) |
else |
t.handler[t.set](t.handler, t.r,t.g,t.b,t.a) |
end |
end |
else |
if type(t.set) == "function" then |
if t.passValue then |
t.set(t.passValue, t.r,t.g,t.b) |
else |
t.set(t.r,t.g,t.b) |
end |
else |
if type(t.handler[t.set]) ~= "function" then |
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.set))) |
end |
if t.passValue then |
t.handler[t.set](t.handler, t.passValue, t.r,t.g,t.b) |
else |
t.handler[t.set](t.handler, t.r,t.g,t.b) |
end |
end |
end |
for k,v in pairs(t) do |
t[k] = nil |
end |
ColorPickerFrame.cancelFunc = nil |
ColorPickerFrame.func = nil |
ColorPickerFrame.opacityFunc = nil |
end |
end |
ColorPickerFrame.cancelFunc = colorCancelFunc |
ShowUIPanel(ColorPickerFrame) |
end |
return |
elseif kind == "group" then |
if #args == 0 then |
printUsage(self, handler, realOptions, options, path, args, passValue) |
else |
-- invalid argument |
print((options.error or IS_NOT_A_VALID_OPTION_FOR):format(args[1], path), realOptions.cmdName or realOptions.name or self) |
end |
return |
end |
this = _G_this |
if Dewdrop then |
Dewdrop:Refresh() |
end |
end |
local external |
local tmp |
function AceConsole:RegisterChatCommand(...) -- slashCommands, options, name |
local slashCommands, options, name |
if type((...)) == "string" then |
if not tmp then |
tmp = {} |
else |
for i in ipairs(tmp) do |
tmp[i] = nil |
end |
end |
for i = 1, select('#', ...)+1 do |
local v = select(i, ...) |
if type(v) == "string" then |
tmp[#tmp+1] = v |
else |
slashCommands = tmp |
options = v |
name = select(i+1, ...) |
break |
end |
end |
else |
slashCommands, options, name = ... |
end |
if type(slashCommands) ~= "table" and slashCommands ~= false then |
AceConsole:error("Bad argument #2 to `RegisterChatCommand' (expected table, got %s)", type(slashCommands)) |
end |
if not slashCommands and type(name) ~= "string" then |
AceConsole:error("Bad argument #4 to `RegisterChatCommand' (expected string, got %s)", type(name)) |
end |
if type(options) ~= "table" and type(options) ~= "function" and options ~= nil then |
AceConsole:error("Bad argument #3 to `RegisterChatCommand' (expected table, function, or nil, got %s)", type(options)) |
end |
if name then |
if type(name) ~= "string" then |
AceConsole:error("Bad argument #4 to `RegisterChatCommand' (expected string or nil, got %s)", type(name)) |
elseif not name:find("^%w+$") or name:upper() ~= name or name:len() == 0 then |
AceConsole:error("Argument #4 must be an uppercase, letters-only string with at least 1 character") |
end |
end |
if slashCommands then |
if #slashCommands == 0 then |
AceConsole:error("Argument #2 to `RegisterChatCommand' must include at least one string") |
end |
for k,v in pairs(slashCommands) do |
if type(k) ~= "number" then |
AceConsole:error("All keys in argument #2 to `RegisterChatCommand' must be numbers") |
end |
if type(v) ~= "string" then |
AceConsole:error("All values in argument #2 to `RegisterChatCommand' must be strings") |
elseif not v:find("^/[A-Za-z][A-Za-z0-9_]*$") then |
AceConsole:error("All values in argument #2 to `RegisterChatCommand' must be in the form of \"/word\"") |
end |
end |
end |
if not options then |
options = { |
type = 'group', |
args = {}, |
handler = self |
} |
end |
if type(options) == "table" then |
local err, position = validateOptions(options) |
if err then |
if position then |
AceConsole:error(position .. ": " .. err) |
else |
AceConsole:error(err) |
end |
end |
if not options.handler then |
options.handler = self |
end |
if options.handler == self and options.type:lower() == "group" and self.class then |
AceConsole:InjectAceOptionsTable(self, options) |
end |
end |
local chat |
if slashCommands then |
chat = slashCommands[1] |
else |
chat = _G["SLASH_"..name..1] |
end |
local handler |
if type(options) == "function" then |
handler = options |
for k,v in pairs(_G) do |
if handler == v then |
local k = k |
handler = function(msg) |
return _G[k](msg) |
end |
end |
end |
else |
function handler(msg) |
handlerFunc(self, chat, msg, options) |
end |
end |
if not _G.SlashCmdList then |
_G.SlashCmdList = {} |
end |
if not name and type(slashCommands) == "table" and type(slashCommands[1]) == "string" then |
name = slashCommands[1]:gsub("%A", ""):upper() |
end |
if not name then |
local A = ('A'):byte() |
repeat |
name = string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) |
until not _G.SlashCmdList[name] |
end |
if slashCommands then |
if _G.SlashCmdList[name] then |
local i = 0 |
while true do |
i = i + 1 |
if _G["SLASH_"..name..i] then |
_G["SLASH_"..name..i] = nil |
else |
break |
end |
end |
end |
local i = 0 |
for _,command in ipairs(slashCommands) do |
i = i + 1 |
_G["SLASH_"..name..i] = command |
if command:lower() ~= command then |
i = i + 1 |
_G["SLASH_"..name..i] = command:lower() |
end |
end |
end |
_G.SlashCmdList[name] = handler |
if self ~= AceConsole and self.slashCommand == nil then |
self.slashCommand = chat |
end |
if not AceEvent and AceLibrary:HasInstance("AceEvent-2.0") then |
external(AceConsole, "AceEvent-2.0", AceLibrary("AceEvent-2.0")) |
end |
if AceEvent then |
if not AceConsole.nextAddon then |
AceConsole.nextAddon = {} |
end |
if type(options) == "table" then |
AceConsole.nextAddon[self] = options |
if not self.playerLogin then |
AceConsole:RegisterEvent("PLAYER_LOGIN", "PLAYER_LOGIN", true) |
end |
end |
end |
AceConsole.registry[name] = options |
if slashCommands == tmp then |
for i in ipairs(tmp) do |
tmp[i] = nil |
end |
end |
end |
function AceConsole:InjectAceOptionsTable(handler, options) |
self:argCheck(handler, 2, "table") |
self:argCheck(options, 3, "table") |
if options.type:lower() ~= "group" then |
self:error('Cannot inject into options table argument #3 if its type is not "group"') |
end |
if options.handler ~= nil and options.handler ~= handler then |
self:error("Cannot inject into options table argument #3 if it has a different handler than argument #2") |
end |
options.handler = handler |
local class = handler.class |
if not AceLibrary:HasInstance("AceOO-2.0") or not class then |
if Rock then |
-- possible Rock object |
for mixin in Rock:IterateObjectMixins(handler) do |
if type(mixin.GetAceOptionsDataTable) == "function" then |
local t = mixin:GetAceOptionsDataTable(handler) |
for k,v in pairs(t) do |
if type(options.args) ~= "table" then |
options.args = {} |
end |
if options.args[k] == nil then |
options.args[k] = v |
end |
end |
end |
end |
end |
else |
-- Ace2 object |
while class and class ~= AceLibrary("AceOO-2.0").Class do |
if type(class.GetAceOptionsDataTable) == "function" then |
local t = class:GetAceOptionsDataTable(handler) |
for k,v in pairs(t) do |
if type(options.args) ~= "table" then |
options.args = {} |
end |
if options.args[k] == nil then |
options.args[k] = v |
end |
end |
end |
local mixins = class.mixins |
if mixins then |
for mixin in pairs(mixins) do |
if type(mixin.GetAceOptionsDataTable) == "function" then |
local t = mixin:GetAceOptionsDataTable(handler) |
for k,v in pairs(t) do |
if type(options.args) ~= "table" then |
options.args = {} |
end |
if options.args[k] == nil then |
options.args[k] = v |
end |
end |
end |
end |
end |
class = class.super |
end |
end |
return options |
end |
function AceConsole:PLAYER_LOGIN() |
self.playerLogin = true |
for addon, options in pairs(self.nextAddon) do |
local err, position = validateOptionsMethods(addon, options) |
if err then |
if position then |
geterrorhandler()(tostring(addon) .. ": AceConsole: " .. position .. ": " .. err) |
else |
geterrorhandler()(tostring(addon) .. ": AceConsole: " .. err) |
end |
end |
self.nextAddon[addon] = nil |
end |
end |
function AceConsole:TabCompleteInfo(cmdpath) |
local cmd = cmdpath:match("(/%S+)") |
if not cmd then |
return |
end |
local path = cmdpath:sub(cmd:len() + 2) |
for name in pairs(SlashCmdList) do --global |
if AceConsole.registry[name] then |
local i = 0 |
while true do |
i = i + 1 |
local scmd = _G["SLASH_"..name..i] |
if not scmd then break end |
if cmd == scmd then |
return name, cmd, path |
end |
end |
end |
end |
end |
function external(self, major, instance) |
if major == "AceEvent-2.0" then |
if not AceEvent then |
AceEvent = instance |
AceEvent:embed(self) |
end |
elseif major == "AceTab-2.0" then |
instance:RegisterTabCompletion("AceConsole", "%/.*", function(t, cmdpath, pos) |
local name, cmd, path = self:TabCompleteInfo(cmdpath:sub(1, pos)) |
if not self.registry[name] then |
return false |
else |
local validArgs, _, _, handler = findTableLevel(self, self.registry[name], cmd, path or "") |
if validArgs.args then |
for arg, v in pairs(validArgs.args) do |
local hidden = v.hidden |
local handler = v.handler or handler |
if hidden then |
if type(hidden) == "function" then |
hidden = hidden(v.passValue) |
elseif type(hidden) == "string" then |
local f = hidden |
local neg = f:match("^~(.-)$") |
if neg then |
f = neg |
end |
if type(handler[f]) ~= "function" then |
self:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f))) |
end |
hidden = handler[f](handler, v.passValue) |
if neg then |
hidden = not hidden |
end |
end |
end |
local disabled = hidden or v.disabled |
if disabled then |
if type(disabled) == "function" then |
disabled = disabled(v.passValue) |
elseif type(disabled) == "string" then |
local f = disabled |
local neg = f:match("^~(.-)$") |
if neg then |
f = neg |
end |
if type(handler[f]) ~= "function" then |
self:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f))) |
end |
disabled = handler[f](handler, v.passValue) |
if neg then |
disabled = not disabled |
end |
end |
end |
if not hidden and not disabled and v.type ~= "header" then |
table.insert(t, (tostring(arg):gsub("%s", "-"))) |
end |
end |
end |
end |
end, function(u, matches, gcs, cmdpath) |
local name, cmd, path = self:TabCompleteInfo(cmdpath) |
if self.registry[name] then |
local validArgs, path2, argwork, handler = findTableLevel(self, self.registry[name], cmd, path) |
printUsage(self, validArgs.handler or handler, self.registry[name], validArgs, path2, argwork, argwork[#argwork], not gcs or gcs ~= "", gcs) |
end |
end) |
elseif major == "Dewdrop-2.0" then |
Dewdrop = instance |
end |
end |
local function activate(self, oldLib, oldDeactivate) |
AceConsole = self |
if oldLib then |
self.registry = oldLib.registry |
self.nextAddon = oldLib.nextAddon |
end |
if not self.registry then |
self.registry = {} |
else |
for name,options in pairs(self.registry) do |
self:RegisterChatCommand(false, options, name) |
end |
end |
self:RegisterChatCommand("/reload", "/rl", "/reloadui", ReloadUI, "RELOAD") |
self:RegisterChatCommand("/gm", ToggleHelpFrame, "GM") |
local t = { "/print", "/echo" } |
local _,_,_,enabled,loadable = GetAddOnInfo("DevTools") |
if not enabled and not loadable then |
table.insert(t, "/dump") |
end |
self:RegisterChatCommand(t, function(text) |
text = text:trim():match("^(.-);*$") |
local f, err = loadstring("AceLibrary('AceConsole-2.0'):PrintLiteral(" .. text .. ")") |
if not f then |
self:Print("|cffff0000Error:|r", err) |
else |
f() |
end |
end, "PRINT") |
self:activate(oldLib, oldDeactivate) |
if oldDeactivate then |
oldDeactivate(oldLib) |
end |
end |
AceLibrary:Register(AceConsole, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) |
## Interface: 20400 |
## Title: Lib: AceConsole-2.0 |
## Notes: AddOn development framework |
## Author: Ace Development Team |
## LoadOnDemand: 1 |
## X-Website: http://www.wowace.com |
## X-Category: Library |
## X-License: LGPL v2.1 + MIT for AceOO-2.0 |
## Dependencies: AceLibrary, AceEvent-2.0, AceOO-2.0 |
AceConsole-2.0.lua |
--------------------------------------------- |
-- StunWatch v2.2 BETA2 |
-- Changes: |
----Aceified |
----Fixed reapplication of stuns |
----Scale feature |
----Diminishing Returns |
----Selective Stun Tracking |
-- TODO: |
----Switch to defaults option in slash menu |
----Fix twitchy checkboxes in Waterfall config GUI |
--------------------------------------------- |
StunWatch = AceLibrary("AceAddon-2.0"):new("AceConsole-2.0", "AceEvent-2.0", "AceDB-2.0") |
local UnitName = UnitName |
local UnitGUID = UnitGUID |
local StunWatch = StunWatch |
--We'll assign this later |
local db |
--This, too |
local sdb = {} |
--For tracking GUIDs so that we can unregister all candybars after a unit dies |
local unitDB = {} |
--For sap |
local sapID |
--Frame, identifier, and libs |
local StunWatchFrame |
local StunWatchName = "StunWatchBars" |
local candy = LibStub:GetLibrary("LibCandyBar") |
local waterfall = AceLibrary:HasInstance("Waterfall-1.0") and AceLibrary("Waterfall-1.0") or nil |
--Remove a candybar based on the ID given |
function StunWatch:KillTimer(id) |
candy:StopCandyBar(id) |
candy:UnregisterCandyBar(id) |
--Get the spell ID and unit GUID from the ID we were given |
local spellName, unitGUID = strsplit(":", id) |
--Remove this timer from the database |
unitDB[unitGUID][spellName] = nil |
end |
--Remove ALL timers from the specified unit |
function StunWatch:KillAllTimers(unitGUID) |
--match found, iterate through the table removing all candybars associated with the unit |
for k, v in pairs(unitDB[unitGUID]) do |
candy:UnregisterCandyBar(v) |
unitDB[unitGUID][k] = nil |
end |
unitDB[unitGUID] = nil |
end |
do |
--Check for sap, this way, we can delete any other sap timers if we happen to be out of range for SPELL_AURA_REMOVED |
local function sap(id) |
if (spellName == StunWatch.db.profile.sdbn[6770]) then |
if (sapID) then |
if candy:IsCandyBarRegistered(sapID) then |
candy:UnregisterCandyBar(sapID) |
end |
end |
sapID = id |
end |
end |
function StunWatch:StartTimer(spellName, unitGUID, unitName, duration) |
--Create a unique ID based on the spell name/unit GUID |
local id = (spellName .. ":" .. unitGUID) |
sap(id) |
--Check if we have a database on the current unit |
if (not unitDB[unitGUID]) then |
--if not, create one with the specified spell |
unitDB[unitGUID] = { [spellName] = id } |
else |
--if there is, add the spell to the DB |
unitDB[unitGUID][spellName] = id |
end |
--Create the text of the bar based on the spell and unit names |
local text = (spellName .. ": " .. unitName) |
local invert = (tostring(db.invert)) |
candy:RegisterCandyBar(id, duration, text, nil, 1, 1, 0) |
candy:SetCandyBarReversed(id, invert) |
candy:RegisterCandyBarWithGroup(id, StunWatchName) |
candy:SetWidth(id, 160) |
candy:SetHeight(id, 10) |
candy:SetCandyBarScale(id, db.scale) |
candy:StartCandyBar(id, true) |
end |
function StunWatch:RetainDebuff(spellName, unitGUID, unitName, duration, maxDuration) |
--This way, we can start a new timer without screwing up existing ones |
local id = (spellName .. ":" .. unitGUID) |
local invert = (tostring(db.invert)) |
candy:SetCandyBarReversed(id, invert) |
candy:SetCandyBarTime(id, maxDuration) |
candy:SetCandyBarTimeLeft(id, duration) |
end |
end |
function StunWatch:CreateMainFrame() |
--Create and edit the frame which the user uses to move the candybar group |
local frame = StunWatchFrame |
frame:SetWidth("170") |
frame:SetHeight("20") |
frame:SetBackdrop({ |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
tile = true, |
tileSize = 16, |
edgeSize = 0, |
insets = { left = 0, right = 0, top = 0, bottom = 0 }, |
}) |
frame:SetMovable(true) |
frame:SetClampedToScreen(true) |
frame:SetPoint("CENTER", UIParent, "CENTER", 0 ,0) |
frame.Text = frame:CreateFontString(nil, "OVERLAY") |
frame.Text:SetFontObject("GameFontNormal") |
frame.Text:SetWidth(170) |
frame.Text:SetHeight(15) |
frame.Text:SetPoint("CENTER") |
frame.Text:SetText(StunWatchName) |
--SetScripts |
frame:SetScript("OnMouseDown", function(self, button) if (button == "LeftButton") then self:StartMoving(); end end) |
frame:SetScript("OnMouseUp", function(self, button) if (button == "LeftButton") then self:StopMovingOrSizing(); end end) |
end |
function StunWatch:Initialize() |
local select = select |
local sdb = { |
-- Cheap Shot |
[1833] = select(1, GetSpellInfo(1833)), |
-- Kidney Shot |
[408] = select(1, GetSpellInfo(408)), |
-- Blind |
[2094] = select(1, GetSpellInfo(2094)), |
-- Gouge |
[38764] = select(1, GetSpellInfo(38764)), |
-- Sap |
[6770] = select(1, GetSpellInfo(6770)), |
-- Expose Armor |
[8647] = select(1, GetSpellInfo(8647)), |
-- Rupture |
[1943] = select(1, GetSpellInfo(1943)), |
-- Garrote |
[703] = select(1, GetSpellInfo(703)), |
-- Mace Stun |
[5530] = select(1, GetSpellInfo(5530)), |
-- Blade Twisting |
[31125] = select(1, GetSpellInfo(31125)), |
--Deadly Throw |
[26679] = select(1, GetSpellInfo(26679)), |
--Imp. Kick |
[18425] = select(1, GetSpellInfo(18425)), |
--Riposte |
[14251] = select(1, GetSpellInfo(14251)), |
} |
--set up our database |
self.defaults = { |
lock = true, |
grow = true, |
invert = 1, |
target = true, |
focus = true, |
scale = 1, |
sdbn = {}, |
} |
--Set up spell name database, for checking against spells in the UNIT_AURA event, and various other places |
for k,v in pairs(sdb) do |
self.defaults.sdbn[v] = 1 |
end |
self:RegisterDB("StunWatchDB") |
self:RegisterDefaults("profile", self.defaults) |
db = self.db.profile |
local frame = StunWatchFrame |
--set up our options |
self.opts = { |
type ='group', |
name = "StunWatch", |
args = { |
lock = { |
type = 'toggle', |
name = "Lock", |
desc = "Lock StunWatch", |
order = 2, |
get = function() return db.lock end, |
set = function(v) |
db.lock = v |
if (not db.lock) then |
frame:EnableMouse(true) |
frame:Show() |
else |
frame:EnableMouse(false) |
frame:Hide() |
end |
end, |
}, |
invert = { |
type = 'toggle', |
name = "Invert", |
desc = "Invert filling of bars", |
order = 3, |
get = function() return db.invert end, |
set = function(v) |
if (not v) then |
db.invert = false |
else |
db.invert = "1" |
end |
end, |
}, |
grow = { |
type = 'toggle', |
name = "Grow Up", |
desc = "Toggle Growth up and down", |
order = 4, |
get = function() return db.grow end, |
set = function(v) |
if (v) then |
candy:SetCandyBarGroupGrowth(StunWatchName, true) -- bars grow upwards |
else |
candy:SetCandyBarGroupGrowth(StunWatchName, false) --bars grow downwards |
end |
db.grow = v |
end, |
}, |
target = { |
type = 'toggle', |
name = "Target (recommended)", |
desc = "Track Stuns/Debuffs on your target", |
order = 5, |
get = function() return db.target end, |
set = function(v) |
if (not v) then |
db.target = false |
self:UnregisterEvent("PLAYER_TARGET_CHANGED", "TargetFocusUpdate") |
else |
db.target = true |
self:RegisterEvent("PLAYER_TARGET_CHANGED", "TargetFocusUpdate") |
end |
end, |
}, |
focus = { |
type = 'toggle', |
name = "Focus", |
desc = "Track Stuns/Debuffs on your focus target", |
order = 6, |
get = function() return db.focus end, |
set = function(v) |
if (not v) then |
db.focus = false |
self:UnregisterEvent("PLAYER_FOCUS_CHANGED", "TargetFocusUpdate") |
else |
db.focus = true |
self:RegisterEvent("PLAYER_FOCUS_CHANGED", "TargetFocusUpdate") |
end |
end, |
}, |
scale = { |
type = 'range', |
name = "Scale", |
desc = "Scale of StunWatch bars", |
order = 7, |
get = function() return db.scale end, |
set = function(v) |
db.scale = v |
frame:SetScale(v) |
end, |
min = .5, |
max = 1.5, |
step = .1, |
}, |
debuffFilter = { |
type = 'group', |
name = "Debuffs", |
desc = "Select which debuffs to track", |
order = 8, |
args = { |
cheapshot = { |
type = 'toggle', |
name = sdb[1833], |
desc = "Toggle " .. sdb[1833], |
order = 9, |
get = function() return db.sdbn[sdb[1833]] end, |
set = function(v) |
if (not v) then |
db.sdbn[sdb[1833]] = false |
else |
db.sdbn[sdb[1833]] = 1 |
end |
end, |
}, |
kidneyshot = { |
type = 'toggle', |
name = sdb[408], |
desc = "Toggle " .. sdb[408], |
order = 10, |
get = function() return db.sdbn[sdb[408]] end, |
set = function(v) |
if (not v) then |
db.sdbn[sdb[408]] = false |
else |
db.sdbn[sdb[408]] = 1 |
end |
end, |
}, |
blind = { |
type = 'toggle', |
name = sdb[2094], |
desc = "Toggle " .. sdb[2094], |
order = 11, |
get = function() return db.sdbn[sdb[2094]] end, |
set = function(v) |
if (not v) then |
db.sdbn[sdb[2094]] = false |
else |
db.sdbn[sdb[2094]] = 1 |
end |
end, |
}, |
gouge = { |
type = 'toggle', |
name = sdb[38764], |
desc = "Toggle " .. sdb[38764], |
order = 12, |
get = function() return db.sdbn[sdb[38764]] end, |
set = function(v) |
if (not v) then |
db.sdbn[sdb[38764]] = false |
else |
db.sdbn[sdb[38764]] = 1 |
end |
end, |
}, |
sap = { |
type = 'toggle', |
name = sdb[6770], |
desc = "Toggle " .. sdb[6770], |
order = 13, |
get = function() return db.sdbn[sdb[6770]] end, |
set = function(v) |
if (not v) then |
db.sdbn[sdb[6770]] = false |
else |
db.sdbn[sdb[6770]] = 1 |
end |
end, |
}, |
exposearmor = { |
type = 'toggle', |
name = sdb[8647], |
desc = "Toggle " .. sdb[8647], |
order = 14, |
get = function() return db.sdbn[sdb[8647]] end, |
set = function(v) |
if (not v) then |
db.sdbn[sdb[8647]] = false |
else |
db.sdbn[sdb[8647]] = 1 |
end |
end, |
}, |
rupture = { |
type = 'toggle', |
name = sdb[1943], |
desc = "Toggle " .. sdb[1943], |
order = 15, |
get = function() return db.sdbn[sdb[1943]] end, |
set = function(v) |
if (not v) then |
db.sdbn[sdb[1943]] = false |
else |
db.sdbn[sdb[1943]] = 1 |
end |
end, |
}, |
garrote = { |
type = 'toggle', |
name = sdb[703], |
desc = "Toggle " .. sdb[703], |
order = 16, |
get = function() return db.sdbn[sdb[703]] end, |
set = function(v) |
if (not v) then |
db.sdbn[sdb[703]] = false |
else |
db.sdbn[sdb[703]] = 1 |
end |
end, |
}, |
macestun = { |
type = 'toggle', |
name = sdb[5530], |
desc = "Toggle " .. sdb[5530], |
order = 17, |
get = function() return db.sdbn[sdb[5530]] end, |
set = function(v) |
if (not v) then |
db.sdbn[sdb[5530]] = false |
else |
db.sdbn[sdb[5530]] = 1 |
end |
end, |
}, |
bladetwisting = { |
type = 'toggle', |
name = sdb[31125], |
desc = "Toggle " .. sdb[31125], |
order = 18, |
get = function() return db.sdbn[sdb[31125]] end, |
set = function(v) |
if (not v) then |
db.sdbn[sdb[31125]] = false |
else |
db.sdbn[sdb[31125]] = 1 |
end |
end, |
}, |
deadlythrow = { |
type = 'toggle', |
name = sdb[26679], |
desc = "Toggle " .. sdb[26679], |
order = 19, |
get = function() return db.sdbn[sdb[26679]] end, |
set = function(v) |
if (not v) then |
db.sdbn[sdb[26679]] = false |
else |
db.sdbn[sdb[26679]] = 1 |
end |
end, |
}, |
impkick = { |
type = 'toggle', |
name = sdb[18425], |
desc = "Toggle " .. sdb[18425], |
order = 20, |
get = function() return db.sdbn[sdb[18425]] end, |
set = function(v) |
if (not v) then |
db.sdbn[sdb[18425]] = false |
else |
db.sdbn[sdb[18425]] = 1 |
end |
end, |
}, |
riposte = { |
type = 'toggle', |
name = sdb[14251], |
desc = "Toggle " .. sdb[14251], |
order = 21, |
get = function() return db.sdbn[sdb[14251]] end, |
set = function(v) |
if (not v) then |
db.sdbn[sdb[14251]] = false |
else |
db.sdbn[sdb[14251]] = 1 |
end |
end, |
}, |
}, |
}, |
}, |
} |
--Set up our slash command, depending upon waterfall's availibility |
if (waterfall) then |
waterfall:Register("StunWatch", "aceOptions", self.opts, "colorR", 0.2, "colorG", 0.3, "colorB", 0.6) |
self:RegisterChatCommand("/sw", "/stunwatch", function() waterfall:Open("StunWatch") end) |
else |
self:RegisterChatCommand("/sw", "/stunwatch", self.opts) |
end |
if (not db.lock) then |
frame:EnableMouse(true) |
frame:Show() |
else |
frame:Hide() |
frame:EnableMouse(false) |
end |
local name = StunWatchName |
if (db.grow) then |
candy:SetCandyBarGroupGrowth(name, true) --bars grow upwards /please test |
else |
candy:SetCandyBarGroupGrowth(name, false) --bars grow downwards /please test |
end |
if (not db.lock) then |
frame:Show() |
else |
frame:Hide() |
end |
candy:RegisterCandyBarGroup(name) |
candy:SetCandyBarGroupPoint(name, "BOTTOM", frame, "BOTTOM", 0, 0) |
end |
function StunWatch:UpdateBars(token) |
for i=1,40 do |
spellName, _, _, _, _, maxDuration, timeLeft = UnitDebuff(token, i) |
if (not spellName) then |
return |
end |
--Check if it's a spell we want to track |
if (db.sdbn[spellName] and timeLeft and timeLeft > 0) then |
--We need these variables to check for and start timers |
local GUID, name = UnitGUID(token), UnitName(token) |
if (not candy:IsCandyBarRegistered(spellName .. ":" .. GUID)) then |
self:StartTimer(spellName, GUID, name, timeLeft) |
else |
self:RetainDebuff(spellName, GUID, name, timeLeft, maxDuration) |
end |
end |
end |
end |
function StunWatch:UNIT_AURA(token) |
if (db[token]) then |
self:UpdateBars(token) |
end |
end |
function StunWatch:COMBAT_LOG_EVENT_UNFILTERED(...) |
--Retrieve the specific combat event |
local _, cmbEvent = select(1, ...) |
if (cmbEvent == "UNIT_DIED") then |
--Check if there are any timers for the unit that died, if there are, clear them all |
local dstGUID = select(6, ...) |
if (unitDB[dstGUID]) then |
self:KillAllTimers(dstGUID) |
end |
elseif ((cmbEvent == "SPELL_AURA_REMOVED") or (cmbEvent == "SPELL_AURA_DISPELLED")) then |
--Clear any debuff timers associated with the removed debuff |
local dstGUID, _, _, _, spellName = select(6, ...) |
local id = (spellName .. ":" .. dstGUID) |
if candy:IsCandyBarRegistered(id) then |
self:KillTimer(id) |
end |
end |
end |
function StunWatch:TargetFocusUpdate() |
self:UpdateBars("target") |
self:UpdateBars("focus") |
end |
function StunWatch:OnInitialize() |
self:CreateMainFrame() |
self:Initialize() |
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") |
self:RegisterEvent("PLAYER_TARGET_CHANGED", "TargetFocusUpdate") |
self:RegisterEvent("PLAYER_FOCUS_CHANGED", "TargetFocusUpdate") |
self:RegisterEvent("UNIT_AURA") |
end |
StunWatchFrame = CreateFrame("Frame", "StunWatchMainFrame", UIParent) |
## Interface: 20400 |
## Title: StunWatch |
## Author: Macca |
## Version: 2.1 |
## Author: nwkegan (Formerly Maccaa) |
## Version: 2.2 |
## Notes: Displays a progress bar for Rogue abilites |
## SavedVariables: StunWatchDB |
## OptionalDeps: Ace2, LibStub, LibCandyBar, Waterfall-1.0 |
## X-Embeds: Ace2, LibStub, LibCandyBar, Waterfall-1.0 |
## X-Credits: WoWUIDev and WoWAce |
## X-WoWIPortal: nwkegan |
## X-RelSite-WoWI: 8677 |
Libs\LibStub\LibStub.lua |
Libs\LibCandyBar\LibCandyBar.lua |
StunWatch.lua |
Libs\AceLibrary\AceLibrary.lua |
Libs\AceAddon-2.0\AceAddon-2.0.lua |
Libs\AceConsole-2.0\AceConsole-2.0.lua |
Libs\AceDB-2.0\AceDB-2.0.lua |
Libs\AceEvent-2.0\AceEvent-2.0.lua |
Libs\Waterfall-1.0\Waterfall-1.0\Waterfall-1.0.lua |
SWcore.lua |